162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2016-2022 HabanaLabs, Ltd.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <uapi/drm/habanalabs_accel.h>
962306a36Sopenharmony_ci#include "habanalabs.h"
1062306a36Sopenharmony_ci#include "../include/hw_ip/mmu/mmu_general.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/uaccess.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/vmalloc.h>
1562306a36Sopenharmony_ci#include <linux/pci-p2pdma.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define HL_MMU_DEBUG	0
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */
2262306a36Sopenharmony_ci#define DRAM_POOL_PAGE_SIZE	SZ_8M
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define MEM_HANDLE_INVALID	ULONG_MAX
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int allocate_timestamps_buffers(struct hl_fpriv *hpriv,
2762306a36Sopenharmony_ci			struct hl_mem_in *args, u64 *handle);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int set_alloc_page_size(struct hl_device *hdev, struct hl_mem_in *args, u32 *page_size)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
3262306a36Sopenharmony_ci	u64 psize;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/*
3562306a36Sopenharmony_ci	 * for ASIC that supports setting the allocation page size by user we will address
3662306a36Sopenharmony_ci	 * user's choice only if it is not 0 (as 0 means taking the default page size)
3762306a36Sopenharmony_ci	 */
3862306a36Sopenharmony_ci	if (prop->supports_user_set_page_size && args->alloc.page_size) {
3962306a36Sopenharmony_ci		psize = args->alloc.page_size;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		if (!is_power_of_2(psize)) {
4262306a36Sopenharmony_ci			dev_err(hdev->dev, "user page size (%#llx) is not power of 2\n", psize);
4362306a36Sopenharmony_ci			return -EINVAL;
4462306a36Sopenharmony_ci		}
4562306a36Sopenharmony_ci	} else {
4662306a36Sopenharmony_ci		psize = prop->device_mem_alloc_default_page_size;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	*page_size = psize;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * The va ranges in context object contain a list with the available chunks of
5662306a36Sopenharmony_ci * device virtual memory.
5762306a36Sopenharmony_ci * There is one range for host allocations and one for DRAM allocations.
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * On initialization each range contains one chunk of all of its available
6062306a36Sopenharmony_ci * virtual range which is a half of the total device virtual range.
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * On each mapping of physical pages, a suitable virtual range chunk (with a
6362306a36Sopenharmony_ci * minimum size) is selected from the list. If the chunk size equals the
6462306a36Sopenharmony_ci * requested size, the chunk is returned. Otherwise, the chunk is split into
6562306a36Sopenharmony_ci * two chunks - one to return as result and a remainder to stay in the list.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * On each Unmapping of a virtual address, the relevant virtual chunk is
6862306a36Sopenharmony_ci * returned to the list. The chunk is added to the list and if its edges match
6962306a36Sopenharmony_ci * the edges of the adjacent chunks (means a contiguous chunk can be created),
7062306a36Sopenharmony_ci * the chunks are merged.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * On finish, the list is checked to have only one chunk of all the relevant
7362306a36Sopenharmony_ci * virtual range (which is a half of the device total virtual range).
7462306a36Sopenharmony_ci * If not (means not all mappings were unmapped), a warning is printed.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * alloc_device_memory() - allocate device memory.
7962306a36Sopenharmony_ci * @ctx: pointer to the context structure.
8062306a36Sopenharmony_ci * @args: host parameters containing the requested size.
8162306a36Sopenharmony_ci * @ret_handle: result handle.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * This function does the following:
8462306a36Sopenharmony_ci * - Allocate the requested size rounded up to 'dram_page_size' pages.
8562306a36Sopenharmony_ci * - Return unique handle for later map/unmap/free.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
8862306a36Sopenharmony_ci				u32 *ret_handle)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
9162306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
9262306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
9362306a36Sopenharmony_ci	u64 paddr = 0, total_size, num_pgs, i;
9462306a36Sopenharmony_ci	u32 num_curr_pgs, page_size;
9562306a36Sopenharmony_ci	bool contiguous;
9662306a36Sopenharmony_ci	int handle, rc;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	num_curr_pgs = 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	rc = set_alloc_page_size(hdev, args, &page_size);
10162306a36Sopenharmony_ci	if (rc)
10262306a36Sopenharmony_ci		return rc;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	num_pgs = DIV_ROUND_UP_ULL(args->alloc.mem_size, page_size);
10562306a36Sopenharmony_ci	total_size = num_pgs * page_size;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!total_size) {
10862306a36Sopenharmony_ci		dev_err(hdev->dev, "Cannot allocate 0 bytes\n");
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	contiguous = args->flags & HL_MEM_CONTIGUOUS;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (contiguous) {
11562306a36Sopenharmony_ci		if (is_power_of_2(page_size))
11662306a36Sopenharmony_ci			paddr = (uintptr_t) gen_pool_dma_alloc_align(vm->dram_pg_pool,
11762306a36Sopenharmony_ci								     total_size, NULL, page_size);
11862306a36Sopenharmony_ci		else
11962306a36Sopenharmony_ci			paddr = gen_pool_alloc(vm->dram_pg_pool, total_size);
12062306a36Sopenharmony_ci		if (!paddr) {
12162306a36Sopenharmony_ci			dev_err(hdev->dev,
12262306a36Sopenharmony_ci				"Cannot allocate %llu contiguous pages with total size of %llu\n",
12362306a36Sopenharmony_ci				num_pgs, total_size);
12462306a36Sopenharmony_ci			return -ENOMEM;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL);
12962306a36Sopenharmony_ci	if (!phys_pg_pack) {
13062306a36Sopenharmony_ci		rc = -ENOMEM;
13162306a36Sopenharmony_ci		goto pages_pack_err;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	phys_pg_pack->vm_type = VM_TYPE_PHYS_PACK;
13562306a36Sopenharmony_ci	phys_pg_pack->asid = ctx->asid;
13662306a36Sopenharmony_ci	phys_pg_pack->npages = num_pgs;
13762306a36Sopenharmony_ci	phys_pg_pack->page_size = page_size;
13862306a36Sopenharmony_ci	phys_pg_pack->total_size = total_size;
13962306a36Sopenharmony_ci	phys_pg_pack->flags = args->flags;
14062306a36Sopenharmony_ci	phys_pg_pack->contiguous = contiguous;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	phys_pg_pack->pages = kvmalloc_array(num_pgs, sizeof(u64), GFP_KERNEL);
14362306a36Sopenharmony_ci	if (ZERO_OR_NULL_PTR(phys_pg_pack->pages)) {
14462306a36Sopenharmony_ci		rc = -ENOMEM;
14562306a36Sopenharmony_ci		goto pages_arr_err;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (phys_pg_pack->contiguous) {
14962306a36Sopenharmony_ci		for (i = 0 ; i < num_pgs ; i++)
15062306a36Sopenharmony_ci			phys_pg_pack->pages[i] = paddr + i * page_size;
15162306a36Sopenharmony_ci	} else {
15262306a36Sopenharmony_ci		for (i = 0 ; i < num_pgs ; i++) {
15362306a36Sopenharmony_ci			if (is_power_of_2(page_size))
15462306a36Sopenharmony_ci				phys_pg_pack->pages[i] =
15562306a36Sopenharmony_ci					(uintptr_t)gen_pool_dma_alloc_align(vm->dram_pg_pool,
15662306a36Sopenharmony_ci									    page_size, NULL,
15762306a36Sopenharmony_ci									    page_size);
15862306a36Sopenharmony_ci			else
15962306a36Sopenharmony_ci				phys_pg_pack->pages[i] = gen_pool_alloc(vm->dram_pg_pool,
16062306a36Sopenharmony_ci									page_size);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci			if (!phys_pg_pack->pages[i]) {
16362306a36Sopenharmony_ci				dev_err(hdev->dev,
16462306a36Sopenharmony_ci					"Cannot allocate device memory (out of memory)\n");
16562306a36Sopenharmony_ci				rc = -ENOMEM;
16662306a36Sopenharmony_ci				goto page_err;
16762306a36Sopenharmony_ci			}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci			num_curr_pgs++;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	spin_lock(&vm->idr_lock);
17462306a36Sopenharmony_ci	handle = idr_alloc(&vm->phys_pg_pack_handles, phys_pg_pack, 1, 0,
17562306a36Sopenharmony_ci				GFP_ATOMIC);
17662306a36Sopenharmony_ci	spin_unlock(&vm->idr_lock);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (handle < 0) {
17962306a36Sopenharmony_ci		dev_err(hdev->dev, "Failed to get handle for page\n");
18062306a36Sopenharmony_ci		rc = -EFAULT;
18162306a36Sopenharmony_ci		goto idr_err;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	for (i = 0 ; i < num_pgs ; i++)
18562306a36Sopenharmony_ci		kref_get(&vm->dram_pg_pool_refcount);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	phys_pg_pack->handle = handle;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	atomic64_add(phys_pg_pack->total_size, &ctx->dram_phys_mem);
19062306a36Sopenharmony_ci	atomic64_add(phys_pg_pack->total_size, &hdev->dram_used_mem);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	*ret_handle = handle;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ciidr_err:
19762306a36Sopenharmony_cipage_err:
19862306a36Sopenharmony_ci	if (!phys_pg_pack->contiguous)
19962306a36Sopenharmony_ci		for (i = 0 ; i < num_curr_pgs ; i++)
20062306a36Sopenharmony_ci			gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[i],
20162306a36Sopenharmony_ci					page_size);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	kvfree(phys_pg_pack->pages);
20462306a36Sopenharmony_cipages_arr_err:
20562306a36Sopenharmony_ci	kfree(phys_pg_pack);
20662306a36Sopenharmony_cipages_pack_err:
20762306a36Sopenharmony_ci	if (contiguous)
20862306a36Sopenharmony_ci		gen_pool_free(vm->dram_pg_pool, paddr, total_size);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return rc;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/**
21462306a36Sopenharmony_ci * dma_map_host_va() - DMA mapping of the given host virtual address.
21562306a36Sopenharmony_ci * @hdev: habanalabs device structure.
21662306a36Sopenharmony_ci * @addr: the host virtual address of the memory area.
21762306a36Sopenharmony_ci * @size: the size of the memory area.
21862306a36Sopenharmony_ci * @p_userptr: pointer to result userptr structure.
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * This function does the following:
22162306a36Sopenharmony_ci * - Allocate userptr structure.
22262306a36Sopenharmony_ci * - Pin the given host memory using the userptr structure.
22362306a36Sopenharmony_ci * - Perform DMA mapping to have the DMA addresses of the pages.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size,
22662306a36Sopenharmony_ci				struct hl_userptr **p_userptr)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct hl_userptr *userptr;
22962306a36Sopenharmony_ci	int rc;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	userptr = kzalloc(sizeof(*userptr), GFP_KERNEL);
23262306a36Sopenharmony_ci	if (!userptr) {
23362306a36Sopenharmony_ci		rc = -ENOMEM;
23462306a36Sopenharmony_ci		goto userptr_err;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	rc = hl_pin_host_memory(hdev, addr, size, userptr);
23862306a36Sopenharmony_ci	if (rc)
23962306a36Sopenharmony_ci		goto pin_err;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	userptr->dma_mapped = true;
24262306a36Sopenharmony_ci	userptr->dir = DMA_BIDIRECTIONAL;
24362306a36Sopenharmony_ci	userptr->vm_type = VM_TYPE_USERPTR;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	*p_userptr = userptr;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	rc = hdev->asic_funcs->asic_dma_map_sgtable(hdev, userptr->sgt, DMA_BIDIRECTIONAL);
24862306a36Sopenharmony_ci	if (rc) {
24962306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to map sgt with DMA region\n");
25062306a36Sopenharmony_ci		goto dma_map_err;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cidma_map_err:
25662306a36Sopenharmony_ci	hl_unpin_host_memory(hdev, userptr);
25762306a36Sopenharmony_cipin_err:
25862306a36Sopenharmony_ci	kfree(userptr);
25962306a36Sopenharmony_ciuserptr_err:
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return rc;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/**
26562306a36Sopenharmony_ci * dma_unmap_host_va() - DMA unmapping of the given host virtual address.
26662306a36Sopenharmony_ci * @hdev: habanalabs device structure.
26762306a36Sopenharmony_ci * @userptr: userptr to free.
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * This function does the following:
27062306a36Sopenharmony_ci * - Unpins the physical pages.
27162306a36Sopenharmony_ci * - Frees the userptr structure.
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic void dma_unmap_host_va(struct hl_device *hdev,
27462306a36Sopenharmony_ci				struct hl_userptr *userptr)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	hl_unpin_host_memory(hdev, userptr);
27762306a36Sopenharmony_ci	kfree(userptr);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * dram_pg_pool_do_release() - free DRAM pages pool
28262306a36Sopenharmony_ci * @ref: pointer to reference object.
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * This function does the following:
28562306a36Sopenharmony_ci * - Frees the idr structure of physical pages handles.
28662306a36Sopenharmony_ci * - Frees the generic pool of DRAM physical pages.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic void dram_pg_pool_do_release(struct kref *ref)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct hl_vm *vm = container_of(ref, struct hl_vm,
29162306a36Sopenharmony_ci			dram_pg_pool_refcount);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * free the idr here as only here we know for sure that there are no
29562306a36Sopenharmony_ci	 * allocated physical pages and hence there are no handles in use
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	idr_destroy(&vm->phys_pg_pack_handles);
29862306a36Sopenharmony_ci	gen_pool_destroy(vm->dram_pg_pool);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/**
30262306a36Sopenharmony_ci * free_phys_pg_pack() - free physical page pack.
30362306a36Sopenharmony_ci * @hdev: habanalabs device structure.
30462306a36Sopenharmony_ci * @phys_pg_pack: physical page pack to free.
30562306a36Sopenharmony_ci *
30662306a36Sopenharmony_ci * This function does the following:
30762306a36Sopenharmony_ci * - For DRAM memory only
30862306a36Sopenharmony_ci *   - iterate over the pack, free each physical block structure by
30962306a36Sopenharmony_ci *     returning it to the general pool.
31062306a36Sopenharmony_ci * - Free the hl_vm_phys_pg_pack structure.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_cistatic void free_phys_pg_pack(struct hl_device *hdev,
31362306a36Sopenharmony_ci				struct hl_vm_phys_pg_pack *phys_pg_pack)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
31662306a36Sopenharmony_ci	u64 i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (phys_pg_pack->created_from_userptr)
31962306a36Sopenharmony_ci		goto end;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (phys_pg_pack->contiguous) {
32262306a36Sopenharmony_ci		gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[0],
32362306a36Sopenharmony_ci			phys_pg_pack->total_size);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		for (i = 0; i < phys_pg_pack->npages ; i++)
32662306a36Sopenharmony_ci			kref_put(&vm->dram_pg_pool_refcount,
32762306a36Sopenharmony_ci				dram_pg_pool_do_release);
32862306a36Sopenharmony_ci	} else {
32962306a36Sopenharmony_ci		for (i = 0 ; i < phys_pg_pack->npages ; i++) {
33062306a36Sopenharmony_ci			gen_pool_free(vm->dram_pg_pool,
33162306a36Sopenharmony_ci				phys_pg_pack->pages[i],
33262306a36Sopenharmony_ci				phys_pg_pack->page_size);
33362306a36Sopenharmony_ci			kref_put(&vm->dram_pg_pool_refcount,
33462306a36Sopenharmony_ci				dram_pg_pool_do_release);
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ciend:
33962306a36Sopenharmony_ci	kvfree(phys_pg_pack->pages);
34062306a36Sopenharmony_ci	kfree(phys_pg_pack);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/**
34662306a36Sopenharmony_ci * free_device_memory() - free device memory.
34762306a36Sopenharmony_ci * @ctx: pointer to the context structure.
34862306a36Sopenharmony_ci * @args: host parameters containing the requested size.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * This function does the following:
35162306a36Sopenharmony_ci * - Free the device memory related to the given handle.
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_cistatic int free_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
35662306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
35762306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
35862306a36Sopenharmony_ci	u32 handle = args->free.handle;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	spin_lock(&vm->idr_lock);
36162306a36Sopenharmony_ci	phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
36262306a36Sopenharmony_ci	if (!phys_pg_pack) {
36362306a36Sopenharmony_ci		spin_unlock(&vm->idr_lock);
36462306a36Sopenharmony_ci		dev_err(hdev->dev, "free device memory failed, no match for handle %u\n", handle);
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (atomic_read(&phys_pg_pack->mapping_cnt) > 0) {
36962306a36Sopenharmony_ci		spin_unlock(&vm->idr_lock);
37062306a36Sopenharmony_ci		dev_err(hdev->dev, "handle %u is mapped, cannot free\n", handle);
37162306a36Sopenharmony_ci		return -EINVAL;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* must remove from idr before the freeing of the physical pages as the refcount of the pool
37562306a36Sopenharmony_ci	 * is also the trigger of the idr destroy
37662306a36Sopenharmony_ci	 */
37762306a36Sopenharmony_ci	idr_remove(&vm->phys_pg_pack_handles, handle);
37862306a36Sopenharmony_ci	spin_unlock(&vm->idr_lock);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	atomic64_sub(phys_pg_pack->total_size, &ctx->dram_phys_mem);
38162306a36Sopenharmony_ci	atomic64_sub(phys_pg_pack->total_size, &hdev->dram_used_mem);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	free_phys_pg_pack(hdev, phys_pg_pack);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/**
38962306a36Sopenharmony_ci * clear_va_list_locked() - free virtual addresses list.
39062306a36Sopenharmony_ci * @hdev: habanalabs device structure.
39162306a36Sopenharmony_ci * @va_list: list of virtual addresses to free.
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci * This function does the following:
39462306a36Sopenharmony_ci * - Iterate over the list and free each virtual addresses block.
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * This function should be called only when va_list lock is taken.
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic void clear_va_list_locked(struct hl_device *hdev,
39962306a36Sopenharmony_ci		struct list_head *va_list)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct hl_vm_va_block *va_block, *tmp;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	list_for_each_entry_safe(va_block, tmp, va_list, node) {
40462306a36Sopenharmony_ci		list_del(&va_block->node);
40562306a36Sopenharmony_ci		kfree(va_block);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/**
41062306a36Sopenharmony_ci * print_va_list_locked() - print virtual addresses list.
41162306a36Sopenharmony_ci * @hdev: habanalabs device structure.
41262306a36Sopenharmony_ci * @va_list: list of virtual addresses to print.
41362306a36Sopenharmony_ci *
41462306a36Sopenharmony_ci * This function does the following:
41562306a36Sopenharmony_ci * - Iterate over the list and print each virtual addresses block.
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * This function should be called only when va_list lock is taken.
41862306a36Sopenharmony_ci */
41962306a36Sopenharmony_cistatic void print_va_list_locked(struct hl_device *hdev,
42062306a36Sopenharmony_ci		struct list_head *va_list)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci#if HL_MMU_DEBUG
42362306a36Sopenharmony_ci	struct hl_vm_va_block *va_block;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	dev_dbg(hdev->dev, "print va list:\n");
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	list_for_each_entry(va_block, va_list, node)
42862306a36Sopenharmony_ci		dev_dbg(hdev->dev,
42962306a36Sopenharmony_ci			"va block, start: 0x%llx, end: 0x%llx, size: %llu\n",
43062306a36Sopenharmony_ci			va_block->start, va_block->end, va_block->size);
43162306a36Sopenharmony_ci#endif
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/**
43562306a36Sopenharmony_ci * merge_va_blocks_locked() - merge a virtual block if possible.
43662306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
43762306a36Sopenharmony_ci * @va_list: pointer to the virtual addresses block list.
43862306a36Sopenharmony_ci * @va_block: virtual block to merge with adjacent blocks.
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * This function does the following:
44162306a36Sopenharmony_ci * - Merge the given blocks with the adjacent blocks if their virtual ranges
44262306a36Sopenharmony_ci *   create a contiguous virtual range.
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * This Function should be called only when va_list lock is taken.
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_cistatic void merge_va_blocks_locked(struct hl_device *hdev,
44762306a36Sopenharmony_ci		struct list_head *va_list, struct hl_vm_va_block *va_block)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct hl_vm_va_block *prev, *next;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	prev = list_prev_entry(va_block, node);
45262306a36Sopenharmony_ci	if (&prev->node != va_list && prev->end + 1 == va_block->start) {
45362306a36Sopenharmony_ci		prev->end = va_block->end;
45462306a36Sopenharmony_ci		prev->size = prev->end - prev->start + 1;
45562306a36Sopenharmony_ci		list_del(&va_block->node);
45662306a36Sopenharmony_ci		kfree(va_block);
45762306a36Sopenharmony_ci		va_block = prev;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	next = list_next_entry(va_block, node);
46162306a36Sopenharmony_ci	if (&next->node != va_list && va_block->end + 1 == next->start) {
46262306a36Sopenharmony_ci		next->start = va_block->start;
46362306a36Sopenharmony_ci		next->size = next->end - next->start + 1;
46462306a36Sopenharmony_ci		list_del(&va_block->node);
46562306a36Sopenharmony_ci		kfree(va_block);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/**
47062306a36Sopenharmony_ci * add_va_block_locked() - add a virtual block to the virtual addresses list.
47162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
47262306a36Sopenharmony_ci * @va_list: pointer to the virtual addresses block list.
47362306a36Sopenharmony_ci * @start: start virtual address.
47462306a36Sopenharmony_ci * @end: end virtual address.
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * This function does the following:
47762306a36Sopenharmony_ci * - Add the given block to the virtual blocks list and merge with other blocks
47862306a36Sopenharmony_ci *   if a contiguous virtual block can be created.
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * This Function should be called only when va_list lock is taken.
48162306a36Sopenharmony_ci */
48262306a36Sopenharmony_cistatic int add_va_block_locked(struct hl_device *hdev,
48362306a36Sopenharmony_ci		struct list_head *va_list, u64 start, u64 end)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct hl_vm_va_block *va_block, *res = NULL;
48662306a36Sopenharmony_ci	u64 size = end - start + 1;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	print_va_list_locked(hdev, va_list);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	list_for_each_entry(va_block, va_list, node) {
49162306a36Sopenharmony_ci		/* TODO: remove upon matureness */
49262306a36Sopenharmony_ci		if (hl_mem_area_crosses_range(start, size, va_block->start,
49362306a36Sopenharmony_ci				va_block->end)) {
49462306a36Sopenharmony_ci			dev_err(hdev->dev,
49562306a36Sopenharmony_ci				"block crossing ranges at start 0x%llx, end 0x%llx\n",
49662306a36Sopenharmony_ci				va_block->start, va_block->end);
49762306a36Sopenharmony_ci			return -EINVAL;
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		if (va_block->end < start)
50162306a36Sopenharmony_ci			res = va_block;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	va_block = kmalloc(sizeof(*va_block), GFP_KERNEL);
50562306a36Sopenharmony_ci	if (!va_block)
50662306a36Sopenharmony_ci		return -ENOMEM;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	va_block->start = start;
50962306a36Sopenharmony_ci	va_block->end = end;
51062306a36Sopenharmony_ci	va_block->size = size;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (!res)
51362306a36Sopenharmony_ci		list_add(&va_block->node, va_list);
51462306a36Sopenharmony_ci	else
51562306a36Sopenharmony_ci		list_add(&va_block->node, &res->node);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	merge_va_blocks_locked(hdev, va_list, va_block);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	print_va_list_locked(hdev, va_list);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/**
52562306a36Sopenharmony_ci * add_va_block() - wrapper for add_va_block_locked.
52662306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
52762306a36Sopenharmony_ci * @va_range: pointer to the virtual addresses range object.
52862306a36Sopenharmony_ci * @start: start virtual address.
52962306a36Sopenharmony_ci * @end: end virtual address.
53062306a36Sopenharmony_ci *
53162306a36Sopenharmony_ci * This function does the following:
53262306a36Sopenharmony_ci * - Takes the list lock and calls add_va_block_locked.
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_cistatic inline int add_va_block(struct hl_device *hdev,
53562306a36Sopenharmony_ci		struct hl_va_range *va_range, u64 start, u64 end)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int rc;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	mutex_lock(&va_range->lock);
54062306a36Sopenharmony_ci	rc = add_va_block_locked(hdev, &va_range->list, start, end);
54162306a36Sopenharmony_ci	mutex_unlock(&va_range->lock);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return rc;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci/**
54762306a36Sopenharmony_ci * is_hint_crossing_range() - check if hint address crossing specified reserved.
54862306a36Sopenharmony_ci * @range_type: virtual space range type.
54962306a36Sopenharmony_ci * @start_addr: start virtual address.
55062306a36Sopenharmony_ci * @size: block size.
55162306a36Sopenharmony_ci * @prop: asic properties structure to retrieve reserved ranges from.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic inline bool is_hint_crossing_range(enum hl_va_range_type range_type,
55462306a36Sopenharmony_ci		u64 start_addr, u32 size, struct asic_fixed_properties *prop) {
55562306a36Sopenharmony_ci	bool range_cross;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (range_type == HL_VA_RANGE_TYPE_DRAM)
55862306a36Sopenharmony_ci		range_cross =
55962306a36Sopenharmony_ci			hl_mem_area_crosses_range(start_addr, size,
56062306a36Sopenharmony_ci			prop->hints_dram_reserved_va_range.start_addr,
56162306a36Sopenharmony_ci			prop->hints_dram_reserved_va_range.end_addr);
56262306a36Sopenharmony_ci	else if (range_type == HL_VA_RANGE_TYPE_HOST)
56362306a36Sopenharmony_ci		range_cross =
56462306a36Sopenharmony_ci			hl_mem_area_crosses_range(start_addr,	size,
56562306a36Sopenharmony_ci			prop->hints_host_reserved_va_range.start_addr,
56662306a36Sopenharmony_ci			prop->hints_host_reserved_va_range.end_addr);
56762306a36Sopenharmony_ci	else
56862306a36Sopenharmony_ci		range_cross =
56962306a36Sopenharmony_ci			hl_mem_area_crosses_range(start_addr, size,
57062306a36Sopenharmony_ci			prop->hints_host_hpage_reserved_va_range.start_addr,
57162306a36Sopenharmony_ci			prop->hints_host_hpage_reserved_va_range.end_addr);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return range_cross;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/**
57762306a36Sopenharmony_ci * get_va_block() - get a virtual block for the given size and alignment.
57862306a36Sopenharmony_ci *
57962306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
58062306a36Sopenharmony_ci * @va_range: pointer to the virtual addresses range.
58162306a36Sopenharmony_ci * @size: requested block size.
58262306a36Sopenharmony_ci * @hint_addr: hint for requested address by the user.
58362306a36Sopenharmony_ci * @va_block_align: required alignment of the virtual block start address.
58462306a36Sopenharmony_ci * @range_type: va range type (host, dram)
58562306a36Sopenharmony_ci * @flags: additional memory flags, currently only uses HL_MEM_FORCE_HINT
58662306a36Sopenharmony_ci *
58762306a36Sopenharmony_ci * This function does the following:
58862306a36Sopenharmony_ci * - Iterate on the virtual block list to find a suitable virtual block for the
58962306a36Sopenharmony_ci *   given size, hint address and alignment.
59062306a36Sopenharmony_ci * - Reserve the requested block and update the list.
59162306a36Sopenharmony_ci * - Return the start address of the virtual block.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic u64 get_va_block(struct hl_device *hdev,
59462306a36Sopenharmony_ci				struct hl_va_range *va_range,
59562306a36Sopenharmony_ci				u64 size, u64 hint_addr, u32 va_block_align,
59662306a36Sopenharmony_ci				enum hl_va_range_type range_type,
59762306a36Sopenharmony_ci				u32 flags)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct hl_vm_va_block *va_block, *new_va_block = NULL;
60062306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
60162306a36Sopenharmony_ci	u64 tmp_hint_addr, valid_start, valid_size, prev_start, prev_end,
60262306a36Sopenharmony_ci		align_mask, reserved_valid_start = 0, reserved_valid_size = 0,
60362306a36Sopenharmony_ci		dram_hint_mask = prop->dram_hints_align_mask;
60462306a36Sopenharmony_ci	bool add_prev = false;
60562306a36Sopenharmony_ci	bool is_align_pow_2  = is_power_of_2(va_range->page_size);
60662306a36Sopenharmony_ci	bool is_hint_dram_addr = hl_is_dram_va(hdev, hint_addr);
60762306a36Sopenharmony_ci	bool force_hint = flags & HL_MEM_FORCE_HINT;
60862306a36Sopenharmony_ci	int rc;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (is_align_pow_2)
61162306a36Sopenharmony_ci		align_mask = ~((u64)va_block_align - 1);
61262306a36Sopenharmony_ci	else
61362306a36Sopenharmony_ci		/*
61462306a36Sopenharmony_ci		 * with non-power-of-2 range we work only with page granularity
61562306a36Sopenharmony_ci		 * and the start address is page aligned,
61662306a36Sopenharmony_ci		 * so no need for alignment checking.
61762306a36Sopenharmony_ci		 */
61862306a36Sopenharmony_ci		size = DIV_ROUND_UP_ULL(size, va_range->page_size) *
61962306a36Sopenharmony_ci							va_range->page_size;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	tmp_hint_addr = hint_addr & ~dram_hint_mask;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* Check if we need to ignore hint address */
62462306a36Sopenharmony_ci	if ((is_align_pow_2 && (hint_addr & (va_block_align - 1))) ||
62562306a36Sopenharmony_ci			(!is_align_pow_2 && is_hint_dram_addr &&
62662306a36Sopenharmony_ci			do_div(tmp_hint_addr, va_range->page_size))) {
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		if (force_hint) {
62962306a36Sopenharmony_ci			/* Hint must be respected, so here we just fail */
63062306a36Sopenharmony_ci			dev_err(hdev->dev,
63162306a36Sopenharmony_ci				"Hint address 0x%llx is not page aligned - cannot be respected\n",
63262306a36Sopenharmony_ci				hint_addr);
63362306a36Sopenharmony_ci			return 0;
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		dev_dbg(hdev->dev,
63762306a36Sopenharmony_ci			"Hint address 0x%llx will be ignored because it is not aligned\n",
63862306a36Sopenharmony_ci			hint_addr);
63962306a36Sopenharmony_ci		hint_addr = 0;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	mutex_lock(&va_range->lock);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	print_va_list_locked(hdev, &va_range->list);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	list_for_each_entry(va_block, &va_range->list, node) {
64762306a36Sopenharmony_ci		/* Calc the first possible aligned addr */
64862306a36Sopenharmony_ci		valid_start = va_block->start;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		if (is_align_pow_2 && (valid_start & (va_block_align - 1))) {
65162306a36Sopenharmony_ci			valid_start &= align_mask;
65262306a36Sopenharmony_ci			valid_start += va_block_align;
65362306a36Sopenharmony_ci			if (valid_start > va_block->end)
65462306a36Sopenharmony_ci				continue;
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		valid_size = va_block->end - valid_start + 1;
65862306a36Sopenharmony_ci		if (valid_size < size)
65962306a36Sopenharmony_ci			continue;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		/*
66262306a36Sopenharmony_ci		 * In case hint address is 0, and hints_range_reservation
66362306a36Sopenharmony_ci		 * property enabled, then avoid allocating va blocks from the
66462306a36Sopenharmony_ci		 * range reserved for hint addresses
66562306a36Sopenharmony_ci		 */
66662306a36Sopenharmony_ci		if (prop->hints_range_reservation && !hint_addr)
66762306a36Sopenharmony_ci			if (is_hint_crossing_range(range_type, valid_start,
66862306a36Sopenharmony_ci					size, prop))
66962306a36Sopenharmony_ci				continue;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		/* Pick the minimal length block which has the required size */
67262306a36Sopenharmony_ci		if (!new_va_block || (valid_size < reserved_valid_size)) {
67362306a36Sopenharmony_ci			new_va_block = va_block;
67462306a36Sopenharmony_ci			reserved_valid_start = valid_start;
67562306a36Sopenharmony_ci			reserved_valid_size = valid_size;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		if (hint_addr && hint_addr >= valid_start &&
67962306a36Sopenharmony_ci					(hint_addr + size) <= va_block->end) {
68062306a36Sopenharmony_ci			new_va_block = va_block;
68162306a36Sopenharmony_ci			reserved_valid_start = hint_addr;
68262306a36Sopenharmony_ci			reserved_valid_size = valid_size;
68362306a36Sopenharmony_ci			break;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (!new_va_block) {
68862306a36Sopenharmony_ci		dev_err(hdev->dev, "no available va block for size %llu\n",
68962306a36Sopenharmony_ci								size);
69062306a36Sopenharmony_ci		goto out;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (force_hint && reserved_valid_start != hint_addr) {
69462306a36Sopenharmony_ci		/* Hint address must be respected. If we are here - this means
69562306a36Sopenharmony_ci		 * we could not respect it.
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		dev_err(hdev->dev,
69862306a36Sopenharmony_ci			"Hint address 0x%llx could not be respected\n",
69962306a36Sopenharmony_ci			hint_addr);
70062306a36Sopenharmony_ci		reserved_valid_start = 0;
70162306a36Sopenharmony_ci		goto out;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/*
70562306a36Sopenharmony_ci	 * Check if there is some leftover range due to reserving the new
70662306a36Sopenharmony_ci	 * va block, then return it to the main virtual addresses list.
70762306a36Sopenharmony_ci	 */
70862306a36Sopenharmony_ci	if (reserved_valid_start > new_va_block->start) {
70962306a36Sopenharmony_ci		prev_start = new_va_block->start;
71062306a36Sopenharmony_ci		prev_end = reserved_valid_start - 1;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		new_va_block->start = reserved_valid_start;
71362306a36Sopenharmony_ci		new_va_block->size = reserved_valid_size;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		add_prev = true;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (new_va_block->size > size) {
71962306a36Sopenharmony_ci		new_va_block->start += size;
72062306a36Sopenharmony_ci		new_va_block->size = new_va_block->end - new_va_block->start + 1;
72162306a36Sopenharmony_ci	} else {
72262306a36Sopenharmony_ci		list_del(&new_va_block->node);
72362306a36Sopenharmony_ci		kfree(new_va_block);
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (add_prev) {
72762306a36Sopenharmony_ci		rc = add_va_block_locked(hdev, &va_range->list, prev_start, prev_end);
72862306a36Sopenharmony_ci		if (rc) {
72962306a36Sopenharmony_ci			reserved_valid_start = 0;
73062306a36Sopenharmony_ci			goto out;
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	print_va_list_locked(hdev, &va_range->list);
73562306a36Sopenharmony_ciout:
73662306a36Sopenharmony_ci	mutex_unlock(&va_range->lock);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	return reserved_valid_start;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci/*
74262306a36Sopenharmony_ci * hl_reserve_va_block() - reserve a virtual block of a given size.
74362306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
74462306a36Sopenharmony_ci * @ctx: current context
74562306a36Sopenharmony_ci * @type: virtual addresses range type.
74662306a36Sopenharmony_ci * @size: requested block size.
74762306a36Sopenharmony_ci * @alignment: required alignment in bytes of the virtual block start address,
74862306a36Sopenharmony_ci *             0 means no alignment.
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * This function does the following:
75162306a36Sopenharmony_ci * - Iterate on the virtual block list to find a suitable virtual block for the
75262306a36Sopenharmony_ci *   given size and alignment.
75362306a36Sopenharmony_ci * - Reserve the requested block and update the list.
75462306a36Sopenharmony_ci * - Return the start address of the virtual block.
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_ciu64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
75762306a36Sopenharmony_ci		enum hl_va_range_type type, u64 size, u32 alignment)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	return get_va_block(hdev, ctx->va_range[type], size, 0,
76062306a36Sopenharmony_ci			max(alignment, ctx->va_range[type]->page_size),
76162306a36Sopenharmony_ci			type, 0);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/**
76562306a36Sopenharmony_ci * hl_get_va_range_type() - get va_range type for the given address and size.
76662306a36Sopenharmony_ci * @ctx: context to fetch va_range from.
76762306a36Sopenharmony_ci * @address: the start address of the area we want to validate.
76862306a36Sopenharmony_ci * @size: the size in bytes of the area we want to validate.
76962306a36Sopenharmony_ci * @type: returned va_range type.
77062306a36Sopenharmony_ci *
77162306a36Sopenharmony_ci * Return: true if the area is inside a valid range, false otherwise.
77262306a36Sopenharmony_ci */
77362306a36Sopenharmony_cistatic int hl_get_va_range_type(struct hl_ctx *ctx, u64 address, u64 size,
77462306a36Sopenharmony_ci			enum hl_va_range_type *type)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	int i;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX; i++) {
77962306a36Sopenharmony_ci		if (hl_mem_area_inside_range(address, size,
78062306a36Sopenharmony_ci				ctx->va_range[i]->start_addr,
78162306a36Sopenharmony_ci				ctx->va_range[i]->end_addr)) {
78262306a36Sopenharmony_ci			*type = i;
78362306a36Sopenharmony_ci			return 0;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return -EINVAL;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci/**
79162306a36Sopenharmony_ci * hl_unreserve_va_block() - wrapper for add_va_block to unreserve a va block.
79262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure
79362306a36Sopenharmony_ci * @ctx: pointer to the context structure.
79462306a36Sopenharmony_ci * @start_addr: start virtual address.
79562306a36Sopenharmony_ci * @size: number of bytes to unreserve.
79662306a36Sopenharmony_ci *
79762306a36Sopenharmony_ci * This function does the following:
79862306a36Sopenharmony_ci * - Takes the list lock and calls add_va_block_locked.
79962306a36Sopenharmony_ci */
80062306a36Sopenharmony_ciint hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
80162306a36Sopenharmony_ci		u64 start_addr, u64 size)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	enum hl_va_range_type type;
80462306a36Sopenharmony_ci	int rc;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	rc = hl_get_va_range_type(ctx, start_addr, size, &type);
80762306a36Sopenharmony_ci	if (rc) {
80862306a36Sopenharmony_ci		dev_err(hdev->dev,
80962306a36Sopenharmony_ci			"cannot find va_range for va %#llx size %llu",
81062306a36Sopenharmony_ci			start_addr, size);
81162306a36Sopenharmony_ci		return rc;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	rc = add_va_block(hdev, ctx->va_range[type], start_addr,
81562306a36Sopenharmony_ci						start_addr + size - 1);
81662306a36Sopenharmony_ci	if (rc)
81762306a36Sopenharmony_ci		dev_warn(hdev->dev,
81862306a36Sopenharmony_ci			"add va block failed for vaddr: 0x%llx\n", start_addr);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return rc;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/**
82462306a36Sopenharmony_ci * init_phys_pg_pack_from_userptr() - initialize physical page pack from host
82562306a36Sopenharmony_ci *                                    memory
82662306a36Sopenharmony_ci * @ctx: pointer to the context structure.
82762306a36Sopenharmony_ci * @userptr: userptr to initialize from.
82862306a36Sopenharmony_ci * @pphys_pg_pack: result pointer.
82962306a36Sopenharmony_ci * @force_regular_page: tell the function to ignore huge page optimization,
83062306a36Sopenharmony_ci *                      even if possible. Needed for cases where the device VA
83162306a36Sopenharmony_ci *                      is allocated before we know the composition of the
83262306a36Sopenharmony_ci *                      physical pages
83362306a36Sopenharmony_ci *
83462306a36Sopenharmony_ci * This function does the following:
83562306a36Sopenharmony_ci * - Pin the physical pages related to the given virtual block.
83662306a36Sopenharmony_ci * - Create a physical page pack from the physical pages related to the given
83762306a36Sopenharmony_ci *   virtual block.
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
84062306a36Sopenharmony_ci				struct hl_userptr *userptr,
84162306a36Sopenharmony_ci				struct hl_vm_phys_pg_pack **pphys_pg_pack,
84262306a36Sopenharmony_ci				bool force_regular_page)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	u32 npages, page_size = PAGE_SIZE,
84562306a36Sopenharmony_ci		huge_page_size = ctx->hdev->asic_prop.pmmu_huge.page_size;
84662306a36Sopenharmony_ci	u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size);
84762306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
84862306a36Sopenharmony_ci	bool first = true, is_huge_page_opt;
84962306a36Sopenharmony_ci	u64 page_mask, total_npages;
85062306a36Sopenharmony_ci	struct scatterlist *sg;
85162306a36Sopenharmony_ci	dma_addr_t dma_addr;
85262306a36Sopenharmony_ci	int rc, i, j;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL);
85562306a36Sopenharmony_ci	if (!phys_pg_pack)
85662306a36Sopenharmony_ci		return -ENOMEM;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	phys_pg_pack->vm_type = userptr->vm_type;
85962306a36Sopenharmony_ci	phys_pg_pack->created_from_userptr = true;
86062306a36Sopenharmony_ci	phys_pg_pack->asid = ctx->asid;
86162306a36Sopenharmony_ci	atomic_set(&phys_pg_pack->mapping_cnt, 1);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	is_huge_page_opt = (force_regular_page ? false : true);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Only if all dma_addrs are aligned to 2MB and their
86662306a36Sopenharmony_ci	 * sizes is at least 2MB, we can use huge page mapping.
86762306a36Sopenharmony_ci	 * We limit the 2MB optimization to this condition,
86862306a36Sopenharmony_ci	 * since later on we acquire the related VA range as one
86962306a36Sopenharmony_ci	 * consecutive block.
87062306a36Sopenharmony_ci	 */
87162306a36Sopenharmony_ci	total_npages = 0;
87262306a36Sopenharmony_ci	for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
87362306a36Sopenharmony_ci		npages = hl_get_sg_info(sg, &dma_addr);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		total_npages += npages;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		if ((npages % pgs_in_huge_page) ||
87862306a36Sopenharmony_ci					(dma_addr & (huge_page_size - 1)))
87962306a36Sopenharmony_ci			is_huge_page_opt = false;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (is_huge_page_opt) {
88362306a36Sopenharmony_ci		page_size = huge_page_size;
88462306a36Sopenharmony_ci		do_div(total_npages, pgs_in_huge_page);
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	page_mask = ~(((u64) page_size) - 1);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	phys_pg_pack->pages = kvmalloc_array(total_npages, sizeof(u64),
89062306a36Sopenharmony_ci						GFP_KERNEL);
89162306a36Sopenharmony_ci	if (ZERO_OR_NULL_PTR(phys_pg_pack->pages)) {
89262306a36Sopenharmony_ci		rc = -ENOMEM;
89362306a36Sopenharmony_ci		goto page_pack_arr_mem_err;
89462306a36Sopenharmony_ci	}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	phys_pg_pack->npages = total_npages;
89762306a36Sopenharmony_ci	phys_pg_pack->page_size = page_size;
89862306a36Sopenharmony_ci	phys_pg_pack->total_size = total_npages * page_size;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	j = 0;
90162306a36Sopenharmony_ci	for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
90262306a36Sopenharmony_ci		npages = hl_get_sg_info(sg, &dma_addr);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		/* align down to physical page size and save the offset */
90562306a36Sopenharmony_ci		if (first) {
90662306a36Sopenharmony_ci			first = false;
90762306a36Sopenharmony_ci			phys_pg_pack->offset = dma_addr & (page_size - 1);
90862306a36Sopenharmony_ci			dma_addr &= page_mask;
90962306a36Sopenharmony_ci		}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		while (npages) {
91262306a36Sopenharmony_ci			phys_pg_pack->pages[j++] = dma_addr;
91362306a36Sopenharmony_ci			dma_addr += page_size;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci			if (is_huge_page_opt)
91662306a36Sopenharmony_ci				npages -= pgs_in_huge_page;
91762306a36Sopenharmony_ci			else
91862306a36Sopenharmony_ci				npages--;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	*pphys_pg_pack = phys_pg_pack;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	return 0;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cipage_pack_arr_mem_err:
92762306a36Sopenharmony_ci	kfree(phys_pg_pack);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return rc;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/**
93362306a36Sopenharmony_ci * map_phys_pg_pack() - maps the physical page pack..
93462306a36Sopenharmony_ci * @ctx: pointer to the context structure.
93562306a36Sopenharmony_ci * @vaddr: start address of the virtual area to map from.
93662306a36Sopenharmony_ci * @phys_pg_pack: the pack of physical pages to map to.
93762306a36Sopenharmony_ci *
93862306a36Sopenharmony_ci * This function does the following:
93962306a36Sopenharmony_ci * - Maps each chunk of virtual memory to matching physical chunk.
94062306a36Sopenharmony_ci * - Stores number of successful mappings in the given argument.
94162306a36Sopenharmony_ci * - Returns 0 on success, error code otherwise.
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_cistatic int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
94462306a36Sopenharmony_ci				struct hl_vm_phys_pg_pack *phys_pg_pack)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
94762306a36Sopenharmony_ci	u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i;
94862306a36Sopenharmony_ci	u32 page_size = phys_pg_pack->page_size;
94962306a36Sopenharmony_ci	int rc = 0;
95062306a36Sopenharmony_ci	bool is_host_addr;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	for (i = 0 ; i < phys_pg_pack->npages ; i++) {
95362306a36Sopenharmony_ci		paddr = phys_pg_pack->pages[i];
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		rc = hl_mmu_map_page(ctx, next_vaddr, paddr, page_size,
95662306a36Sopenharmony_ci				(i + 1) == phys_pg_pack->npages);
95762306a36Sopenharmony_ci		if (rc) {
95862306a36Sopenharmony_ci			dev_err(hdev->dev,
95962306a36Sopenharmony_ci				"map failed for handle %u, npages: %llu, mapped: %llu",
96062306a36Sopenharmony_ci				phys_pg_pack->handle, phys_pg_pack->npages,
96162306a36Sopenharmony_ci				mapped_pg_cnt);
96262306a36Sopenharmony_ci			goto err;
96362306a36Sopenharmony_ci		}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		mapped_pg_cnt++;
96662306a36Sopenharmony_ci		next_vaddr += page_size;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return 0;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cierr:
97262306a36Sopenharmony_ci	is_host_addr = !hl_is_dram_va(hdev, vaddr);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	next_vaddr = vaddr;
97562306a36Sopenharmony_ci	for (i = 0 ; i < mapped_pg_cnt ; i++) {
97662306a36Sopenharmony_ci		if (hl_mmu_unmap_page(ctx, next_vaddr, page_size,
97762306a36Sopenharmony_ci					(i + 1) == mapped_pg_cnt))
97862306a36Sopenharmony_ci			dev_warn_ratelimited(hdev->dev,
97962306a36Sopenharmony_ci				"failed to unmap handle %u, va: 0x%llx, pa: 0x%llx, page size: %u\n",
98062306a36Sopenharmony_ci					phys_pg_pack->handle, next_vaddr,
98162306a36Sopenharmony_ci					phys_pg_pack->pages[i], page_size);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci		next_vaddr += page_size;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		/*
98662306a36Sopenharmony_ci		 * unmapping on Palladium can be really long, so avoid a CPU
98762306a36Sopenharmony_ci		 * soft lockup bug by sleeping a little between unmapping pages
98862306a36Sopenharmony_ci		 *
98962306a36Sopenharmony_ci		 * In addition, on host num of pages could be huge,
99062306a36Sopenharmony_ci		 * because page size could be 4KB, so when unmapping host
99162306a36Sopenharmony_ci		 * pages sleep every 32K pages to avoid soft lockup
99262306a36Sopenharmony_ci		 */
99362306a36Sopenharmony_ci		if (hdev->pldm || (is_host_addr && (i & 0x7FFF) == 0))
99462306a36Sopenharmony_ci			usleep_range(50, 200);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return rc;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci/**
100162306a36Sopenharmony_ci * unmap_phys_pg_pack() - unmaps the physical page pack.
100262306a36Sopenharmony_ci * @ctx: pointer to the context structure.
100362306a36Sopenharmony_ci * @vaddr: start address of the virtual area to unmap.
100462306a36Sopenharmony_ci * @phys_pg_pack: the pack of physical pages to unmap.
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_cistatic void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
100762306a36Sopenharmony_ci				struct hl_vm_phys_pg_pack *phys_pg_pack)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
101062306a36Sopenharmony_ci	u64 next_vaddr, i;
101162306a36Sopenharmony_ci	bool is_host_addr;
101262306a36Sopenharmony_ci	u32 page_size;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	is_host_addr = !hl_is_dram_va(hdev, vaddr);
101562306a36Sopenharmony_ci	page_size = phys_pg_pack->page_size;
101662306a36Sopenharmony_ci	next_vaddr = vaddr;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) {
101962306a36Sopenharmony_ci		if (hl_mmu_unmap_page(ctx, next_vaddr, page_size,
102062306a36Sopenharmony_ci				       (i + 1) == phys_pg_pack->npages))
102162306a36Sopenharmony_ci			dev_warn_ratelimited(hdev->dev,
102262306a36Sopenharmony_ci			"unmap failed for vaddr: 0x%llx\n", next_vaddr);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		/*
102562306a36Sopenharmony_ci		 * unmapping on Palladium can be really long, so avoid a CPU
102662306a36Sopenharmony_ci		 * soft lockup bug by sleeping a little between unmapping pages
102762306a36Sopenharmony_ci		 *
102862306a36Sopenharmony_ci		 * In addition, on host num of pages could be huge,
102962306a36Sopenharmony_ci		 * because page size could be 4KB, so when unmapping host
103062306a36Sopenharmony_ci		 * pages sleep every 32K pages to avoid soft lockup
103162306a36Sopenharmony_ci		 */
103262306a36Sopenharmony_ci		if (hdev->pldm || (is_host_addr && (i & 0x7FFF) == 0))
103362306a36Sopenharmony_ci			usleep_range(50, 200);
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/**
103862306a36Sopenharmony_ci * map_device_va() - map the given memory.
103962306a36Sopenharmony_ci * @ctx: pointer to the context structure.
104062306a36Sopenharmony_ci * @args: host parameters with handle/host virtual address.
104162306a36Sopenharmony_ci * @device_addr: pointer to result device virtual address.
104262306a36Sopenharmony_ci *
104362306a36Sopenharmony_ci * This function does the following:
104462306a36Sopenharmony_ci * - If given a physical device memory handle, map to a device virtual block
104562306a36Sopenharmony_ci *   and return the start address of this block.
104662306a36Sopenharmony_ci * - If given a host virtual address and size, find the related physical pages,
104762306a36Sopenharmony_ci *   map a device virtual block to this pages and return the start address of
104862306a36Sopenharmony_ci *   this block.
104962306a36Sopenharmony_ci */
105062306a36Sopenharmony_cistatic int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device_addr)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
105362306a36Sopenharmony_ci	enum hl_va_range_type va_range_type = 0;
105462306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
105562306a36Sopenharmony_ci	struct hl_userptr *userptr = NULL;
105662306a36Sopenharmony_ci	u32 handle = 0, va_block_align;
105762306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode;
105862306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
105962306a36Sopenharmony_ci	struct hl_va_range *va_range;
106062306a36Sopenharmony_ci	bool is_userptr, do_prefetch;
106162306a36Sopenharmony_ci	u64 ret_vaddr, hint_addr;
106262306a36Sopenharmony_ci	enum vm_type *vm_type;
106362306a36Sopenharmony_ci	int rc;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* set map flags */
106662306a36Sopenharmony_ci	is_userptr = args->flags & HL_MEM_USERPTR;
106762306a36Sopenharmony_ci	do_prefetch = hdev->supports_mmu_prefetch && (args->flags & HL_MEM_PREFETCH);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Assume failure */
107062306a36Sopenharmony_ci	*device_addr = 0;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (is_userptr) {
107362306a36Sopenharmony_ci		u64 addr = args->map_host.host_virt_addr,
107462306a36Sopenharmony_ci			size = args->map_host.mem_size;
107562306a36Sopenharmony_ci		u32 page_size = hdev->asic_prop.pmmu.page_size,
107662306a36Sopenharmony_ci			huge_page_size = hdev->asic_prop.pmmu_huge.page_size;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		rc = dma_map_host_va(hdev, addr, size, &userptr);
107962306a36Sopenharmony_ci		if (rc)
108062306a36Sopenharmony_ci			return rc;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		rc = init_phys_pg_pack_from_userptr(ctx, userptr,
108362306a36Sopenharmony_ci				&phys_pg_pack, false);
108462306a36Sopenharmony_ci		if (rc) {
108562306a36Sopenharmony_ci			dev_err(hdev->dev,
108662306a36Sopenharmony_ci				"unable to init page pack for vaddr 0x%llx\n",
108762306a36Sopenharmony_ci				addr);
108862306a36Sopenharmony_ci			goto init_page_pack_err;
108962306a36Sopenharmony_ci		}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		vm_type = (enum vm_type *) userptr;
109262306a36Sopenharmony_ci		hint_addr = args->map_host.hint_addr;
109362306a36Sopenharmony_ci		handle = phys_pg_pack->handle;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		/* get required alignment */
109662306a36Sopenharmony_ci		if (phys_pg_pack->page_size == page_size) {
109762306a36Sopenharmony_ci			va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST];
109862306a36Sopenharmony_ci			va_range_type = HL_VA_RANGE_TYPE_HOST;
109962306a36Sopenharmony_ci			/*
110062306a36Sopenharmony_ci			 * huge page alignment may be needed in case of regular
110162306a36Sopenharmony_ci			 * page mapping, depending on the host VA alignment
110262306a36Sopenharmony_ci			 */
110362306a36Sopenharmony_ci			if (addr & (huge_page_size - 1))
110462306a36Sopenharmony_ci				va_block_align = page_size;
110562306a36Sopenharmony_ci			else
110662306a36Sopenharmony_ci				va_block_align = huge_page_size;
110762306a36Sopenharmony_ci		} else {
110862306a36Sopenharmony_ci			/*
110962306a36Sopenharmony_ci			 * huge page alignment is needed in case of huge page
111062306a36Sopenharmony_ci			 * mapping
111162306a36Sopenharmony_ci			 */
111262306a36Sopenharmony_ci			va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE];
111362306a36Sopenharmony_ci			va_range_type = HL_VA_RANGE_TYPE_HOST_HUGE;
111462306a36Sopenharmony_ci			va_block_align = huge_page_size;
111562306a36Sopenharmony_ci		}
111662306a36Sopenharmony_ci	} else {
111762306a36Sopenharmony_ci		handle = lower_32_bits(args->map_device.handle);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		spin_lock(&vm->idr_lock);
112062306a36Sopenharmony_ci		phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
112162306a36Sopenharmony_ci		if (!phys_pg_pack) {
112262306a36Sopenharmony_ci			spin_unlock(&vm->idr_lock);
112362306a36Sopenharmony_ci			dev_err(hdev->dev,
112462306a36Sopenharmony_ci				"no match for handle %u\n", handle);
112562306a36Sopenharmony_ci			return -EINVAL;
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		/* increment now to avoid freeing device memory while mapping */
112962306a36Sopenharmony_ci		atomic_inc(&phys_pg_pack->mapping_cnt);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		spin_unlock(&vm->idr_lock);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		vm_type = (enum vm_type *) phys_pg_pack;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci		hint_addr = args->map_device.hint_addr;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci		/* DRAM VA alignment is the same as the MMU page size */
113862306a36Sopenharmony_ci		va_range = ctx->va_range[HL_VA_RANGE_TYPE_DRAM];
113962306a36Sopenharmony_ci		va_range_type = HL_VA_RANGE_TYPE_DRAM;
114062306a36Sopenharmony_ci		va_block_align = hdev->asic_prop.dmmu.page_size;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/*
114462306a36Sopenharmony_ci	 * relevant for mapping device physical memory only, as host memory is
114562306a36Sopenharmony_ci	 * implicitly shared
114662306a36Sopenharmony_ci	 */
114762306a36Sopenharmony_ci	if (!is_userptr && !(phys_pg_pack->flags & HL_MEM_SHARED) &&
114862306a36Sopenharmony_ci			phys_pg_pack->asid != ctx->asid) {
114962306a36Sopenharmony_ci		dev_err(hdev->dev,
115062306a36Sopenharmony_ci			"Failed to map memory, handle %u is not shared\n",
115162306a36Sopenharmony_ci			handle);
115262306a36Sopenharmony_ci		rc = -EPERM;
115362306a36Sopenharmony_ci		goto shared_err;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	hnode = kzalloc(sizeof(*hnode), GFP_KERNEL);
115762306a36Sopenharmony_ci	if (!hnode) {
115862306a36Sopenharmony_ci		rc = -ENOMEM;
115962306a36Sopenharmony_ci		goto hnode_err;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (hint_addr && phys_pg_pack->offset) {
116362306a36Sopenharmony_ci		if (args->flags & HL_MEM_FORCE_HINT) {
116462306a36Sopenharmony_ci			/* Fail if hint must be respected but it can't be */
116562306a36Sopenharmony_ci			dev_err(hdev->dev,
116662306a36Sopenharmony_ci				"Hint address 0x%llx cannot be respected because source memory is not aligned 0x%x\n",
116762306a36Sopenharmony_ci				hint_addr, phys_pg_pack->offset);
116862306a36Sopenharmony_ci			rc = -EINVAL;
116962306a36Sopenharmony_ci			goto va_block_err;
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci		dev_dbg(hdev->dev,
117262306a36Sopenharmony_ci			"Hint address 0x%llx will be ignored because source memory is not aligned 0x%x\n",
117362306a36Sopenharmony_ci			hint_addr, phys_pg_pack->offset);
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	ret_vaddr = get_va_block(hdev, va_range, phys_pg_pack->total_size,
117762306a36Sopenharmony_ci					hint_addr, va_block_align,
117862306a36Sopenharmony_ci					va_range_type, args->flags);
117962306a36Sopenharmony_ci	if (!ret_vaddr) {
118062306a36Sopenharmony_ci		dev_err(hdev->dev, "no available va block for handle %u\n",
118162306a36Sopenharmony_ci				handle);
118262306a36Sopenharmony_ci		rc = -ENOMEM;
118362306a36Sopenharmony_ci		goto va_block_err;
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	mutex_lock(&hdev->mmu_lock);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack);
118962306a36Sopenharmony_ci	if (rc) {
119062306a36Sopenharmony_ci		dev_err(hdev->dev, "mapping page pack failed for handle %u\n", handle);
119162306a36Sopenharmony_ci		mutex_unlock(&hdev->mmu_lock);
119262306a36Sopenharmony_ci		goto map_err;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	rc = hl_mmu_invalidate_cache_range(hdev, false, *vm_type | MMU_OP_SKIP_LOW_CACHE_INV,
119662306a36Sopenharmony_ci				ctx->asid, ret_vaddr, phys_pg_pack->total_size);
119762306a36Sopenharmony_ci	mutex_unlock(&hdev->mmu_lock);
119862306a36Sopenharmony_ci	if (rc)
119962306a36Sopenharmony_ci		goto map_err;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/*
120262306a36Sopenharmony_ci	 * prefetch is done upon user's request. it is performed in WQ as and so can
120362306a36Sopenharmony_ci	 * be outside the MMU lock. the operation itself is already protected by the mmu lock
120462306a36Sopenharmony_ci	 */
120562306a36Sopenharmony_ci	if (do_prefetch) {
120662306a36Sopenharmony_ci		rc = hl_mmu_prefetch_cache_range(ctx, *vm_type, ctx->asid, ret_vaddr,
120762306a36Sopenharmony_ci							phys_pg_pack->total_size);
120862306a36Sopenharmony_ci		if (rc)
120962306a36Sopenharmony_ci			goto map_err;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	ret_vaddr += phys_pg_pack->offset;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	hnode->ptr = vm_type;
121562306a36Sopenharmony_ci	hnode->vaddr = ret_vaddr;
121662306a36Sopenharmony_ci	hnode->handle = is_userptr ? MEM_HANDLE_INVALID : handle;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	mutex_lock(&ctx->mem_hash_lock);
121962306a36Sopenharmony_ci	hash_add(ctx->mem_hash, &hnode->node, ret_vaddr);
122062306a36Sopenharmony_ci	mutex_unlock(&ctx->mem_hash_lock);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	*device_addr = ret_vaddr;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (is_userptr)
122562306a36Sopenharmony_ci		free_phys_pg_pack(hdev, phys_pg_pack);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return rc;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cimap_err:
123062306a36Sopenharmony_ci	if (add_va_block(hdev, va_range, ret_vaddr,
123162306a36Sopenharmony_ci				ret_vaddr + phys_pg_pack->total_size - 1))
123262306a36Sopenharmony_ci		dev_warn(hdev->dev,
123362306a36Sopenharmony_ci			"release va block failed for handle 0x%x, vaddr: 0x%llx\n",
123462306a36Sopenharmony_ci				handle, ret_vaddr);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_civa_block_err:
123762306a36Sopenharmony_ci	kfree(hnode);
123862306a36Sopenharmony_cihnode_err:
123962306a36Sopenharmony_cishared_err:
124062306a36Sopenharmony_ci	atomic_dec(&phys_pg_pack->mapping_cnt);
124162306a36Sopenharmony_ci	if (is_userptr)
124262306a36Sopenharmony_ci		free_phys_pg_pack(hdev, phys_pg_pack);
124362306a36Sopenharmony_ciinit_page_pack_err:
124462306a36Sopenharmony_ci	if (is_userptr)
124562306a36Sopenharmony_ci		dma_unmap_host_va(hdev, userptr);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return rc;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/* Should be called while the context's mem_hash_lock is taken */
125162306a36Sopenharmony_cistatic struct hl_vm_hash_node *get_vm_hash_node_locked(struct hl_ctx *ctx, u64 vaddr)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	hash_for_each_possible(ctx->mem_hash, hnode, node, vaddr)
125662306a36Sopenharmony_ci		if (vaddr == hnode->vaddr)
125762306a36Sopenharmony_ci			return hnode;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	return NULL;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/**
126362306a36Sopenharmony_ci * unmap_device_va() - unmap the given device virtual address.
126462306a36Sopenharmony_ci * @ctx: pointer to the context structure.
126562306a36Sopenharmony_ci * @args: host parameters with device virtual address to unmap.
126662306a36Sopenharmony_ci * @ctx_free: true if in context free flow, false otherwise.
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci * This function does the following:
126962306a36Sopenharmony_ci * - unmap the physical pages related to the given virtual address.
127062306a36Sopenharmony_ci * - return the device virtual block to the virtual block list.
127162306a36Sopenharmony_ci */
127262306a36Sopenharmony_cistatic int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
127362306a36Sopenharmony_ci				bool ctx_free)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
127662306a36Sopenharmony_ci	u64 vaddr = args->unmap.device_virt_addr;
127762306a36Sopenharmony_ci	struct asic_fixed_properties *prop;
127862306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
127962306a36Sopenharmony_ci	struct hl_userptr *userptr = NULL;
128062306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode;
128162306a36Sopenharmony_ci	struct hl_va_range *va_range;
128262306a36Sopenharmony_ci	enum vm_type *vm_type;
128362306a36Sopenharmony_ci	bool is_userptr;
128462306a36Sopenharmony_ci	int rc = 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	prop = &hdev->asic_prop;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	/* protect from double entrance */
128962306a36Sopenharmony_ci	mutex_lock(&ctx->mem_hash_lock);
129062306a36Sopenharmony_ci	hnode = get_vm_hash_node_locked(ctx, vaddr);
129162306a36Sopenharmony_ci	if (!hnode) {
129262306a36Sopenharmony_ci		mutex_unlock(&ctx->mem_hash_lock);
129362306a36Sopenharmony_ci		dev_err(hdev->dev, "unmap failed, no mem hnode for vaddr 0x%llx\n", vaddr);
129462306a36Sopenharmony_ci		return -EINVAL;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (hnode->export_cnt) {
129862306a36Sopenharmony_ci		mutex_unlock(&ctx->mem_hash_lock);
129962306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to unmap %#llx, memory is exported\n", vaddr);
130062306a36Sopenharmony_ci		return -EINVAL;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	hash_del(&hnode->node);
130462306a36Sopenharmony_ci	mutex_unlock(&ctx->mem_hash_lock);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	vm_type = hnode->ptr;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (*vm_type == VM_TYPE_USERPTR) {
130962306a36Sopenharmony_ci		is_userptr = true;
131062306a36Sopenharmony_ci		userptr = hnode->ptr;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack,
131362306a36Sopenharmony_ci							false);
131462306a36Sopenharmony_ci		if (rc) {
131562306a36Sopenharmony_ci			dev_err(hdev->dev,
131662306a36Sopenharmony_ci				"unable to init page pack for vaddr 0x%llx\n",
131762306a36Sopenharmony_ci				vaddr);
131862306a36Sopenharmony_ci			goto vm_type_err;
131962306a36Sopenharmony_ci		}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		if (phys_pg_pack->page_size ==
132262306a36Sopenharmony_ci					hdev->asic_prop.pmmu.page_size)
132362306a36Sopenharmony_ci			va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST];
132462306a36Sopenharmony_ci		else
132562306a36Sopenharmony_ci			va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE];
132662306a36Sopenharmony_ci	} else if (*vm_type == VM_TYPE_PHYS_PACK) {
132762306a36Sopenharmony_ci		is_userptr = false;
132862306a36Sopenharmony_ci		va_range = ctx->va_range[HL_VA_RANGE_TYPE_DRAM];
132962306a36Sopenharmony_ci		phys_pg_pack = hnode->ptr;
133062306a36Sopenharmony_ci	} else {
133162306a36Sopenharmony_ci		dev_warn(hdev->dev,
133262306a36Sopenharmony_ci			"unmap failed, unknown vm desc for vaddr 0x%llx\n",
133362306a36Sopenharmony_ci				vaddr);
133462306a36Sopenharmony_ci		rc = -EFAULT;
133562306a36Sopenharmony_ci		goto vm_type_err;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (atomic_read(&phys_pg_pack->mapping_cnt) == 0) {
133962306a36Sopenharmony_ci		dev_err(hdev->dev, "vaddr 0x%llx is not mapped\n", vaddr);
134062306a36Sopenharmony_ci		rc = -EINVAL;
134162306a36Sopenharmony_ci		goto mapping_cnt_err;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (!is_userptr && !is_power_of_2(phys_pg_pack->page_size))
134562306a36Sopenharmony_ci		vaddr = prop->dram_base_address +
134662306a36Sopenharmony_ci			DIV_ROUND_DOWN_ULL(vaddr - prop->dram_base_address,
134762306a36Sopenharmony_ci						phys_pg_pack->page_size) *
134862306a36Sopenharmony_ci							phys_pg_pack->page_size;
134962306a36Sopenharmony_ci	else
135062306a36Sopenharmony_ci		vaddr &= ~(((u64) phys_pg_pack->page_size) - 1);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	mutex_lock(&hdev->mmu_lock);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	/*
135762306a36Sopenharmony_ci	 * During context free this function is called in a loop to clean all
135862306a36Sopenharmony_ci	 * the context mappings. Hence the cache invalidation can be called once
135962306a36Sopenharmony_ci	 * at the loop end rather than for each iteration
136062306a36Sopenharmony_ci	 */
136162306a36Sopenharmony_ci	if (!ctx_free)
136262306a36Sopenharmony_ci		rc = hl_mmu_invalidate_cache_range(hdev, true, *vm_type, ctx->asid, vaddr,
136362306a36Sopenharmony_ci							phys_pg_pack->total_size);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	mutex_unlock(&hdev->mmu_lock);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	/*
136862306a36Sopenharmony_ci	 * If the context is closing we don't need to check for the MMU cache
136962306a36Sopenharmony_ci	 * invalidation return code and update the VA free list as in this flow
137062306a36Sopenharmony_ci	 * we invalidate the MMU cache outside of this unmap function and the VA
137162306a36Sopenharmony_ci	 * free list will be freed anyway.
137262306a36Sopenharmony_ci	 */
137362306a36Sopenharmony_ci	if (!ctx_free) {
137462306a36Sopenharmony_ci		int tmp_rc;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		tmp_rc = add_va_block(hdev, va_range, vaddr,
137762306a36Sopenharmony_ci					vaddr + phys_pg_pack->total_size - 1);
137862306a36Sopenharmony_ci		if (tmp_rc) {
137962306a36Sopenharmony_ci			dev_warn(hdev->dev,
138062306a36Sopenharmony_ci					"add va block failed for vaddr: 0x%llx\n",
138162306a36Sopenharmony_ci					vaddr);
138262306a36Sopenharmony_ci			if (!rc)
138362306a36Sopenharmony_ci				rc = tmp_rc;
138462306a36Sopenharmony_ci		}
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	atomic_dec(&phys_pg_pack->mapping_cnt);
138862306a36Sopenharmony_ci	kfree(hnode);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	if (is_userptr) {
139162306a36Sopenharmony_ci		free_phys_pg_pack(hdev, phys_pg_pack);
139262306a36Sopenharmony_ci		dma_unmap_host_va(hdev, userptr);
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	return rc;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cimapping_cnt_err:
139862306a36Sopenharmony_ci	if (is_userptr)
139962306a36Sopenharmony_ci		free_phys_pg_pack(hdev, phys_pg_pack);
140062306a36Sopenharmony_civm_type_err:
140162306a36Sopenharmony_ci	mutex_lock(&ctx->mem_hash_lock);
140262306a36Sopenharmony_ci	hash_add(ctx->mem_hash, &hnode->node, vaddr);
140362306a36Sopenharmony_ci	mutex_unlock(&ctx->mem_hash_lock);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	return rc;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic int map_block(struct hl_device *hdev, u64 address, u64 *handle, u32 *size)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	u32 block_id;
141162306a36Sopenharmony_ci	int rc;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	*handle = 0;
141462306a36Sopenharmony_ci	if (size)
141562306a36Sopenharmony_ci		*size = 0;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	rc = hdev->asic_funcs->get_hw_block_id(hdev, address, size, &block_id);
141862306a36Sopenharmony_ci	if (rc)
141962306a36Sopenharmony_ci		return rc;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	*handle = block_id | HL_MMAP_TYPE_BLOCK;
142262306a36Sopenharmony_ci	*handle <<= PAGE_SHIFT;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	return 0;
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic void hw_block_vm_close(struct vm_area_struct *vma)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct hl_vm_hw_block_list_node *lnode =
143062306a36Sopenharmony_ci		(struct hl_vm_hw_block_list_node *) vma->vm_private_data;
143162306a36Sopenharmony_ci	struct hl_ctx *ctx = lnode->ctx;
143262306a36Sopenharmony_ci	long new_mmap_size;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	new_mmap_size = lnode->mapped_size - (vma->vm_end - vma->vm_start);
143562306a36Sopenharmony_ci	if (new_mmap_size > 0) {
143662306a36Sopenharmony_ci		lnode->mapped_size = new_mmap_size;
143762306a36Sopenharmony_ci		return;
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	mutex_lock(&ctx->hw_block_list_lock);
144162306a36Sopenharmony_ci	list_del(&lnode->node);
144262306a36Sopenharmony_ci	mutex_unlock(&ctx->hw_block_list_lock);
144362306a36Sopenharmony_ci	hl_ctx_put(ctx);
144462306a36Sopenharmony_ci	kfree(lnode);
144562306a36Sopenharmony_ci	vma->vm_private_data = NULL;
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic const struct vm_operations_struct hw_block_vm_ops = {
144962306a36Sopenharmony_ci	.close = hw_block_vm_close
145062306a36Sopenharmony_ci};
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci/**
145362306a36Sopenharmony_ci * hl_hw_block_mmap() - mmap a hw block to user.
145462306a36Sopenharmony_ci * @hpriv: pointer to the private data of the fd
145562306a36Sopenharmony_ci * @vma: pointer to vm_area_struct of the process
145662306a36Sopenharmony_ci *
145762306a36Sopenharmony_ci * Driver increments context reference for every HW block mapped in order
145862306a36Sopenharmony_ci * to prevent user from closing FD without unmapping first
145962306a36Sopenharmony_ci */
146062306a36Sopenharmony_ciint hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	struct hl_vm_hw_block_list_node *lnode;
146362306a36Sopenharmony_ci	struct hl_device *hdev = hpriv->hdev;
146462306a36Sopenharmony_ci	struct hl_ctx *ctx = hpriv->ctx;
146562306a36Sopenharmony_ci	u32 block_id, block_size;
146662306a36Sopenharmony_ci	int rc;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	/* We use the page offset to hold the block id and thus we need to clear
146962306a36Sopenharmony_ci	 * it before doing the mmap itself
147062306a36Sopenharmony_ci	 */
147162306a36Sopenharmony_ci	block_id = vma->vm_pgoff;
147262306a36Sopenharmony_ci	vma->vm_pgoff = 0;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	/* Driver only allows mapping of a complete HW block */
147562306a36Sopenharmony_ci	block_size = vma->vm_end - vma->vm_start;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (!access_ok((void __user *) (uintptr_t) vma->vm_start, block_size)) {
147862306a36Sopenharmony_ci		dev_err(hdev->dev,
147962306a36Sopenharmony_ci			"user pointer is invalid - 0x%lx\n",
148062306a36Sopenharmony_ci			vma->vm_start);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci		return -EINVAL;
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	lnode = kzalloc(sizeof(*lnode), GFP_KERNEL);
148662306a36Sopenharmony_ci	if (!lnode)
148762306a36Sopenharmony_ci		return -ENOMEM;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	rc = hdev->asic_funcs->hw_block_mmap(hdev, vma, block_id, block_size);
149062306a36Sopenharmony_ci	if (rc) {
149162306a36Sopenharmony_ci		kfree(lnode);
149262306a36Sopenharmony_ci		return rc;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	hl_ctx_get(ctx);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	lnode->ctx = ctx;
149862306a36Sopenharmony_ci	lnode->vaddr = vma->vm_start;
149962306a36Sopenharmony_ci	lnode->block_size = block_size;
150062306a36Sopenharmony_ci	lnode->mapped_size = lnode->block_size;
150162306a36Sopenharmony_ci	lnode->id = block_id;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	vma->vm_private_data = lnode;
150462306a36Sopenharmony_ci	vma->vm_ops = &hw_block_vm_ops;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	mutex_lock(&ctx->hw_block_list_lock);
150762306a36Sopenharmony_ci	list_add_tail(&lnode->node, &ctx->hw_block_mem_list);
150862306a36Sopenharmony_ci	mutex_unlock(&ctx->hw_block_list_lock);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	vma->vm_pgoff = block_id;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return 0;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic int set_dma_sg(struct scatterlist *sg, u64 bar_address, u64 chunk_size,
151662306a36Sopenharmony_ci			struct device *dev, enum dma_data_direction dir)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	dma_addr_t addr;
151962306a36Sopenharmony_ci	int rc;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	addr = dma_map_resource(dev, bar_address, chunk_size, dir,
152262306a36Sopenharmony_ci				DMA_ATTR_SKIP_CPU_SYNC);
152362306a36Sopenharmony_ci	rc = dma_mapping_error(dev, addr);
152462306a36Sopenharmony_ci	if (rc)
152562306a36Sopenharmony_ci		return rc;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	sg_set_page(sg, NULL, chunk_size, 0);
152862306a36Sopenharmony_ci	sg_dma_address(sg) = addr;
152962306a36Sopenharmony_ci	sg_dma_len(sg) = chunk_size;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	return 0;
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_cistatic struct sg_table *alloc_sgt_from_device_pages(struct hl_device *hdev, u64 *pages, u64 npages,
153562306a36Sopenharmony_ci						u64 page_size, u64 exported_size,
153662306a36Sopenharmony_ci						struct device *dev, enum dma_data_direction dir)
153762306a36Sopenharmony_ci{
153862306a36Sopenharmony_ci	u64 chunk_size, bar_address, dma_max_seg_size, cur_size_to_export, cur_npages;
153962306a36Sopenharmony_ci	struct asic_fixed_properties *prop;
154062306a36Sopenharmony_ci	int rc, i, j, nents, cur_page;
154162306a36Sopenharmony_ci	struct scatterlist *sg;
154262306a36Sopenharmony_ci	struct sg_table *sgt;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	prop = &hdev->asic_prop;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	dma_max_seg_size = dma_get_max_seg_size(dev);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	/* We would like to align the max segment size to PAGE_SIZE, so the
154962306a36Sopenharmony_ci	 * SGL will contain aligned addresses that can be easily mapped to
155062306a36Sopenharmony_ci	 * an MMU
155162306a36Sopenharmony_ci	 */
155262306a36Sopenharmony_ci	dma_max_seg_size = ALIGN_DOWN(dma_max_seg_size, PAGE_SIZE);
155362306a36Sopenharmony_ci	if (dma_max_seg_size < PAGE_SIZE) {
155462306a36Sopenharmony_ci		dev_err_ratelimited(hdev->dev,
155562306a36Sopenharmony_ci				"dma_max_seg_size %llu can't be smaller than PAGE_SIZE\n",
155662306a36Sopenharmony_ci				dma_max_seg_size);
155762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
156162306a36Sopenharmony_ci	if (!sgt)
156262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/* remove export size restrictions in case not explicitly defined */
156562306a36Sopenharmony_ci	cur_size_to_export = exported_size ? exported_size : (npages * page_size);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* If the size of each page is larger than the dma max segment size,
156862306a36Sopenharmony_ci	 * then we can't combine pages and the number of entries in the SGL
156962306a36Sopenharmony_ci	 * will just be the
157062306a36Sopenharmony_ci	 * <number of pages> * <chunks of max segment size in each page>
157162306a36Sopenharmony_ci	 */
157262306a36Sopenharmony_ci	if (page_size > dma_max_seg_size) {
157362306a36Sopenharmony_ci		/* we should limit number of pages according to the exported size */
157462306a36Sopenharmony_ci		cur_npages = DIV_ROUND_UP_SECTOR_T(cur_size_to_export, page_size);
157562306a36Sopenharmony_ci		nents = cur_npages * DIV_ROUND_UP_SECTOR_T(page_size, dma_max_seg_size);
157662306a36Sopenharmony_ci	} else {
157762306a36Sopenharmony_ci		cur_npages = npages;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci		/* Get number of non-contiguous chunks */
158062306a36Sopenharmony_ci		for (i = 1, nents = 1, chunk_size = page_size ; i < cur_npages ; i++) {
158162306a36Sopenharmony_ci			if (pages[i - 1] + page_size != pages[i] ||
158262306a36Sopenharmony_ci					chunk_size + page_size > dma_max_seg_size) {
158362306a36Sopenharmony_ci				nents++;
158462306a36Sopenharmony_ci				chunk_size = page_size;
158562306a36Sopenharmony_ci				continue;
158662306a36Sopenharmony_ci			}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci			chunk_size += page_size;
158962306a36Sopenharmony_ci		}
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	rc = sg_alloc_table(sgt, nents, GFP_KERNEL | __GFP_ZERO);
159362306a36Sopenharmony_ci	if (rc)
159462306a36Sopenharmony_ci		goto error_free;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	cur_page = 0;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	if (page_size > dma_max_seg_size) {
159962306a36Sopenharmony_ci		u64 size_left, cur_device_address = 0;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci		size_left = page_size;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci		/* Need to split each page into the number of chunks of
160462306a36Sopenharmony_ci		 * dma_max_seg_size
160562306a36Sopenharmony_ci		 */
160662306a36Sopenharmony_ci		for_each_sgtable_dma_sg(sgt, sg, i) {
160762306a36Sopenharmony_ci			if (size_left == page_size)
160862306a36Sopenharmony_ci				cur_device_address =
160962306a36Sopenharmony_ci					pages[cur_page] - prop->dram_base_address;
161062306a36Sopenharmony_ci			else
161162306a36Sopenharmony_ci				cur_device_address += dma_max_seg_size;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci			/* make sure not to export over exported size */
161462306a36Sopenharmony_ci			chunk_size = min3(size_left, dma_max_seg_size, cur_size_to_export);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci			bar_address = hdev->dram_pci_bar_start + cur_device_address;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci			rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
161962306a36Sopenharmony_ci			if (rc)
162062306a36Sopenharmony_ci				goto error_unmap;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci			cur_size_to_export -= chunk_size;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci			if (size_left > dma_max_seg_size) {
162562306a36Sopenharmony_ci				size_left -= dma_max_seg_size;
162662306a36Sopenharmony_ci			} else {
162762306a36Sopenharmony_ci				cur_page++;
162862306a36Sopenharmony_ci				size_left = page_size;
162962306a36Sopenharmony_ci			}
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci	} else {
163262306a36Sopenharmony_ci		/* Merge pages and put them into the scatterlist */
163362306a36Sopenharmony_ci		for_each_sgtable_dma_sg(sgt, sg, i) {
163462306a36Sopenharmony_ci			chunk_size = page_size;
163562306a36Sopenharmony_ci			for (j = cur_page + 1 ; j < cur_npages ; j++) {
163662306a36Sopenharmony_ci				if (pages[j - 1] + page_size != pages[j] ||
163762306a36Sopenharmony_ci						chunk_size + page_size > dma_max_seg_size)
163862306a36Sopenharmony_ci					break;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci				chunk_size += page_size;
164162306a36Sopenharmony_ci			}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci			bar_address = hdev->dram_pci_bar_start +
164462306a36Sopenharmony_ci					(pages[cur_page] - prop->dram_base_address);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci			/* make sure not to export over exported size */
164762306a36Sopenharmony_ci			chunk_size = min(chunk_size, cur_size_to_export);
164862306a36Sopenharmony_ci			rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
164962306a36Sopenharmony_ci			if (rc)
165062306a36Sopenharmony_ci				goto error_unmap;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci			cur_size_to_export -= chunk_size;
165362306a36Sopenharmony_ci			cur_page = j;
165462306a36Sopenharmony_ci		}
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	/* Because we are not going to include a CPU list we want to have some
165862306a36Sopenharmony_ci	 * chance that other users will detect this by setting the orig_nents
165962306a36Sopenharmony_ci	 * to 0 and using only nents (length of DMA list) when going over the
166062306a36Sopenharmony_ci	 * sgl
166162306a36Sopenharmony_ci	 */
166262306a36Sopenharmony_ci	sgt->orig_nents = 0;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	return sgt;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cierror_unmap:
166762306a36Sopenharmony_ci	for_each_sgtable_dma_sg(sgt, sg, i) {
166862306a36Sopenharmony_ci		if (!sg_dma_len(sg))
166962306a36Sopenharmony_ci			continue;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci		dma_unmap_resource(dev, sg_dma_address(sg),
167262306a36Sopenharmony_ci					sg_dma_len(sg), dir,
167362306a36Sopenharmony_ci					DMA_ATTR_SKIP_CPU_SYNC);
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	sg_free_table(sgt);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cierror_free:
167962306a36Sopenharmony_ci	kfree(sgt);
168062306a36Sopenharmony_ci	return ERR_PTR(rc);
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic int hl_dmabuf_attach(struct dma_buf *dmabuf,
168462306a36Sopenharmony_ci				struct dma_buf_attachment *attachment)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	struct hl_dmabuf_priv *hl_dmabuf;
168762306a36Sopenharmony_ci	struct hl_device *hdev;
168862306a36Sopenharmony_ci	int rc;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	hl_dmabuf = dmabuf->priv;
169162306a36Sopenharmony_ci	hdev = hl_dmabuf->ctx->hdev;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	rc = pci_p2pdma_distance(hdev->pdev, attachment->dev, true);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (rc < 0)
169662306a36Sopenharmony_ci		attachment->peer2peer = false;
169762306a36Sopenharmony_ci	return 0;
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic struct sg_table *hl_map_dmabuf(struct dma_buf_attachment *attachment,
170162306a36Sopenharmony_ci					enum dma_data_direction dir)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	struct dma_buf *dma_buf = attachment->dmabuf;
170462306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
170562306a36Sopenharmony_ci	struct hl_dmabuf_priv *hl_dmabuf;
170662306a36Sopenharmony_ci	struct hl_device *hdev;
170762306a36Sopenharmony_ci	struct sg_table *sgt;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	hl_dmabuf = dma_buf->priv;
171062306a36Sopenharmony_ci	hdev = hl_dmabuf->ctx->hdev;
171162306a36Sopenharmony_ci	phys_pg_pack = hl_dmabuf->phys_pg_pack;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (!attachment->peer2peer) {
171462306a36Sopenharmony_ci		dev_dbg(hdev->dev, "Failed to map dmabuf because p2p is disabled\n");
171562306a36Sopenharmony_ci		return ERR_PTR(-EPERM);
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (phys_pg_pack)
171962306a36Sopenharmony_ci		sgt = alloc_sgt_from_device_pages(hdev,
172062306a36Sopenharmony_ci						phys_pg_pack->pages,
172162306a36Sopenharmony_ci						phys_pg_pack->npages,
172262306a36Sopenharmony_ci						phys_pg_pack->page_size,
172362306a36Sopenharmony_ci						phys_pg_pack->exported_size,
172462306a36Sopenharmony_ci						attachment->dev,
172562306a36Sopenharmony_ci						dir);
172662306a36Sopenharmony_ci	else
172762306a36Sopenharmony_ci		sgt = alloc_sgt_from_device_pages(hdev,
172862306a36Sopenharmony_ci						&hl_dmabuf->device_address,
172962306a36Sopenharmony_ci						1,
173062306a36Sopenharmony_ci						hl_dmabuf->dmabuf->size,
173162306a36Sopenharmony_ci						0,
173262306a36Sopenharmony_ci						attachment->dev,
173362306a36Sopenharmony_ci						dir);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	if (IS_ERR(sgt))
173662306a36Sopenharmony_ci		dev_err(hdev->dev, "failed (%ld) to initialize sgt for dmabuf\n", PTR_ERR(sgt));
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	return sgt;
173962306a36Sopenharmony_ci}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_cistatic void hl_unmap_dmabuf(struct dma_buf_attachment *attachment,
174262306a36Sopenharmony_ci				  struct sg_table *sgt,
174362306a36Sopenharmony_ci				  enum dma_data_direction dir)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci	struct scatterlist *sg;
174662306a36Sopenharmony_ci	int i;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	/* The memory behind the dma-buf has *always* resided on the device itself, i.e. it lives
174962306a36Sopenharmony_ci	 * only in the 'device' domain (after all, it maps a PCI bar address which points to the
175062306a36Sopenharmony_ci	 * device memory).
175162306a36Sopenharmony_ci	 *
175262306a36Sopenharmony_ci	 * Therefore, it was never in the 'CPU' domain and hence, there is no need to perform
175362306a36Sopenharmony_ci	 * a sync of the memory to the CPU's cache, as it never resided inside that cache.
175462306a36Sopenharmony_ci	 */
175562306a36Sopenharmony_ci	for_each_sgtable_dma_sg(sgt, sg, i)
175662306a36Sopenharmony_ci		dma_unmap_resource(attachment->dev, sg_dma_address(sg),
175762306a36Sopenharmony_ci					sg_dma_len(sg), dir,
175862306a36Sopenharmony_ci					DMA_ATTR_SKIP_CPU_SYNC);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	/* Need to restore orig_nents because sg_free_table use that field */
176162306a36Sopenharmony_ci	sgt->orig_nents = sgt->nents;
176262306a36Sopenharmony_ci	sg_free_table(sgt);
176362306a36Sopenharmony_ci	kfree(sgt);
176462306a36Sopenharmony_ci}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_cistatic struct hl_vm_hash_node *memhash_node_export_get(struct hl_ctx *ctx, u64 addr)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
176962306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	/* get the memory handle */
177262306a36Sopenharmony_ci	mutex_lock(&ctx->mem_hash_lock);
177362306a36Sopenharmony_ci	hnode = get_vm_hash_node_locked(ctx, addr);
177462306a36Sopenharmony_ci	if (!hnode) {
177562306a36Sopenharmony_ci		mutex_unlock(&ctx->mem_hash_lock);
177662306a36Sopenharmony_ci		dev_dbg(hdev->dev, "map address %#llx not found\n", addr);
177762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	if (upper_32_bits(hnode->handle)) {
178162306a36Sopenharmony_ci		mutex_unlock(&ctx->mem_hash_lock);
178262306a36Sopenharmony_ci		dev_dbg(hdev->dev, "invalid handle %#llx for map address %#llx\n",
178362306a36Sopenharmony_ci				hnode->handle, addr);
178462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	/*
178862306a36Sopenharmony_ci	 * node found, increase export count so this memory cannot be unmapped
178962306a36Sopenharmony_ci	 * and the hash node cannot be deleted.
179062306a36Sopenharmony_ci	 */
179162306a36Sopenharmony_ci	hnode->export_cnt++;
179262306a36Sopenharmony_ci	mutex_unlock(&ctx->mem_hash_lock);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	return hnode;
179562306a36Sopenharmony_ci}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_cistatic void memhash_node_export_put(struct hl_ctx *ctx, struct hl_vm_hash_node *hnode)
179862306a36Sopenharmony_ci{
179962306a36Sopenharmony_ci	mutex_lock(&ctx->mem_hash_lock);
180062306a36Sopenharmony_ci	hnode->export_cnt--;
180162306a36Sopenharmony_ci	mutex_unlock(&ctx->mem_hash_lock);
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistatic void hl_release_dmabuf(struct dma_buf *dmabuf)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv;
180762306a36Sopenharmony_ci	struct hl_ctx *ctx;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (!hl_dmabuf)
181062306a36Sopenharmony_ci		return;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	ctx = hl_dmabuf->ctx;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (hl_dmabuf->memhash_hnode)
181562306a36Sopenharmony_ci		memhash_node_export_put(ctx, hl_dmabuf->memhash_hnode);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	atomic_dec(&ctx->hdev->dmabuf_export_cnt);
181862306a36Sopenharmony_ci	hl_ctx_put(ctx);
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* Paired with get_file() in export_dmabuf() */
182162306a36Sopenharmony_ci	fput(ctx->hpriv->filp);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	kfree(hl_dmabuf);
182462306a36Sopenharmony_ci}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_cistatic const struct dma_buf_ops habanalabs_dmabuf_ops = {
182762306a36Sopenharmony_ci	.attach = hl_dmabuf_attach,
182862306a36Sopenharmony_ci	.map_dma_buf = hl_map_dmabuf,
182962306a36Sopenharmony_ci	.unmap_dma_buf = hl_unmap_dmabuf,
183062306a36Sopenharmony_ci	.release = hl_release_dmabuf,
183162306a36Sopenharmony_ci};
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_cistatic int export_dmabuf(struct hl_ctx *ctx,
183462306a36Sopenharmony_ci				struct hl_dmabuf_priv *hl_dmabuf,
183562306a36Sopenharmony_ci				u64 total_size, int flags, int *dmabuf_fd)
183662306a36Sopenharmony_ci{
183762306a36Sopenharmony_ci	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
183862306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
183962306a36Sopenharmony_ci	int rc, fd;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	exp_info.ops = &habanalabs_dmabuf_ops;
184262306a36Sopenharmony_ci	exp_info.size = total_size;
184362306a36Sopenharmony_ci	exp_info.flags = flags;
184462306a36Sopenharmony_ci	exp_info.priv = hl_dmabuf;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	hl_dmabuf->dmabuf = dma_buf_export(&exp_info);
184762306a36Sopenharmony_ci	if (IS_ERR(hl_dmabuf->dmabuf)) {
184862306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to export dma-buf\n");
184962306a36Sopenharmony_ci		return PTR_ERR(hl_dmabuf->dmabuf);
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	fd = dma_buf_fd(hl_dmabuf->dmabuf, flags);
185362306a36Sopenharmony_ci	if (fd < 0) {
185462306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf, %d\n", fd);
185562306a36Sopenharmony_ci		rc = fd;
185662306a36Sopenharmony_ci		goto err_dma_buf_put;
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	hl_dmabuf->ctx = ctx;
186062306a36Sopenharmony_ci	hl_ctx_get(hl_dmabuf->ctx);
186162306a36Sopenharmony_ci	atomic_inc(&ctx->hdev->dmabuf_export_cnt);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	/* Get compute device file to enforce release order, such that all exported dma-buf will be
186462306a36Sopenharmony_ci	 * released first and only then the compute device.
186562306a36Sopenharmony_ci	 * Paired with fput() in hl_release_dmabuf().
186662306a36Sopenharmony_ci	 */
186762306a36Sopenharmony_ci	get_file(ctx->hpriv->filp);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	*dmabuf_fd = fd;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	return 0;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_cierr_dma_buf_put:
187462306a36Sopenharmony_ci	hl_dmabuf->dmabuf->priv = NULL;
187562306a36Sopenharmony_ci	dma_buf_put(hl_dmabuf->dmabuf);
187662306a36Sopenharmony_ci	return rc;
187762306a36Sopenharmony_ci}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_cistatic int validate_export_params_common(struct hl_device *hdev, u64 device_addr, u64 size)
188062306a36Sopenharmony_ci{
188162306a36Sopenharmony_ci	if (!IS_ALIGNED(device_addr, PAGE_SIZE)) {
188262306a36Sopenharmony_ci		dev_dbg(hdev->dev,
188362306a36Sopenharmony_ci			"exported device memory address 0x%llx should be aligned to 0x%lx\n",
188462306a36Sopenharmony_ci			device_addr, PAGE_SIZE);
188562306a36Sopenharmony_ci		return -EINVAL;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	if (size < PAGE_SIZE) {
188962306a36Sopenharmony_ci		dev_dbg(hdev->dev,
189062306a36Sopenharmony_ci			"exported device memory size %llu should be equal to or greater than %lu\n",
189162306a36Sopenharmony_ci			size, PAGE_SIZE);
189262306a36Sopenharmony_ci		return -EINVAL;
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	return 0;
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_cistatic int validate_export_params_no_mmu(struct hl_device *hdev, u64 device_addr, u64 size)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
190162306a36Sopenharmony_ci	u64 bar_address;
190262306a36Sopenharmony_ci	int rc;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	rc = validate_export_params_common(hdev, device_addr, size);
190562306a36Sopenharmony_ci	if (rc)
190662306a36Sopenharmony_ci		return rc;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	if (device_addr < prop->dram_user_base_address ||
190962306a36Sopenharmony_ci				(device_addr + size) > prop->dram_end_address ||
191062306a36Sopenharmony_ci				(device_addr + size) < device_addr) {
191162306a36Sopenharmony_ci		dev_dbg(hdev->dev,
191262306a36Sopenharmony_ci			"DRAM memory range 0x%llx (+0x%llx) is outside of DRAM boundaries\n",
191362306a36Sopenharmony_ci			device_addr, size);
191462306a36Sopenharmony_ci		return -EINVAL;
191562306a36Sopenharmony_ci	}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	bar_address = hdev->dram_pci_bar_start + (device_addr - prop->dram_base_address);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	if ((bar_address + size) > (hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
192062306a36Sopenharmony_ci			(bar_address + size) < bar_address) {
192162306a36Sopenharmony_ci		dev_dbg(hdev->dev,
192262306a36Sopenharmony_ci			"DRAM memory range 0x%llx (+0x%llx) is outside of PCI BAR boundaries\n",
192362306a36Sopenharmony_ci			device_addr, size);
192462306a36Sopenharmony_ci		return -EINVAL;
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	return 0;
192862306a36Sopenharmony_ci}
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_cistatic int validate_export_params(struct hl_device *hdev, u64 device_addr, u64 size, u64 offset,
193162306a36Sopenharmony_ci					struct hl_vm_phys_pg_pack *phys_pg_pack)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
193462306a36Sopenharmony_ci	u64 bar_address;
193562306a36Sopenharmony_ci	int i, rc;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	rc = validate_export_params_common(hdev, device_addr, size);
193862306a36Sopenharmony_ci	if (rc)
193962306a36Sopenharmony_ci		return rc;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	if ((offset + size) > phys_pg_pack->total_size) {
194262306a36Sopenharmony_ci		dev_dbg(hdev->dev, "offset %#llx and size %#llx exceed total map size %#llx\n",
194362306a36Sopenharmony_ci				offset, size, phys_pg_pack->total_size);
194462306a36Sopenharmony_ci		return -EINVAL;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	for (i = 0 ; i < phys_pg_pack->npages ; i++) {
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci		bar_address = hdev->dram_pci_bar_start +
195062306a36Sopenharmony_ci					(phys_pg_pack->pages[i] - prop->dram_base_address);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci		if ((bar_address + phys_pg_pack->page_size) >
195362306a36Sopenharmony_ci				(hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
195462306a36Sopenharmony_ci				(bar_address + phys_pg_pack->page_size) < bar_address) {
195562306a36Sopenharmony_ci			dev_dbg(hdev->dev,
195662306a36Sopenharmony_ci				"DRAM memory range 0x%llx (+0x%x) is outside of PCI BAR boundaries\n",
195762306a36Sopenharmony_ci					phys_pg_pack->pages[i],
195862306a36Sopenharmony_ci					phys_pg_pack->page_size);
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci			return -EINVAL;
196162306a36Sopenharmony_ci		}
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return 0;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic struct hl_vm_phys_pg_pack *get_phys_pg_pack_from_hash_node(struct hl_device *hdev,
196862306a36Sopenharmony_ci							struct hl_vm_hash_node *hnode)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack;
197162306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	spin_lock(&vm->idr_lock);
197462306a36Sopenharmony_ci	phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, (u32) hnode->handle);
197562306a36Sopenharmony_ci	if (!phys_pg_pack) {
197662306a36Sopenharmony_ci		spin_unlock(&vm->idr_lock);
197762306a36Sopenharmony_ci		dev_dbg(hdev->dev, "no match for handle 0x%x\n", (u32) hnode->handle);
197862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	spin_unlock(&vm->idr_lock);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	if (phys_pg_pack->vm_type != VM_TYPE_PHYS_PACK) {
198462306a36Sopenharmony_ci		dev_dbg(hdev->dev, "handle 0x%llx does not represent DRAM memory\n", hnode->handle);
198562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	return phys_pg_pack;
198962306a36Sopenharmony_ci}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci/**
199262306a36Sopenharmony_ci * export_dmabuf_from_addr() - export a dma-buf object for the given memory
199362306a36Sopenharmony_ci *                             address and size.
199462306a36Sopenharmony_ci * @ctx: pointer to the context structure.
199562306a36Sopenharmony_ci * @addr: device address.
199662306a36Sopenharmony_ci * @size: size of device memory to export.
199762306a36Sopenharmony_ci * @offset: the offset into the buffer from which to start exporting
199862306a36Sopenharmony_ci * @flags: DMA-BUF file/FD flags.
199962306a36Sopenharmony_ci * @dmabuf_fd: pointer to result FD that represents the dma-buf object.
200062306a36Sopenharmony_ci *
200162306a36Sopenharmony_ci * Create and export a dma-buf object for an existing memory allocation inside
200262306a36Sopenharmony_ci * the device memory, and return a FD which is associated with the dma-buf
200362306a36Sopenharmony_ci * object.
200462306a36Sopenharmony_ci *
200562306a36Sopenharmony_ci * Return: 0 on success, non-zero for failure.
200662306a36Sopenharmony_ci */
200762306a36Sopenharmony_cistatic int export_dmabuf_from_addr(struct hl_ctx *ctx, u64 addr, u64 size, u64 offset,
200862306a36Sopenharmony_ci					int flags, int *dmabuf_fd)
200962306a36Sopenharmony_ci{
201062306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
201162306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode = NULL;
201262306a36Sopenharmony_ci	struct asic_fixed_properties *prop;
201362306a36Sopenharmony_ci	struct hl_dmabuf_priv *hl_dmabuf;
201462306a36Sopenharmony_ci	struct hl_device *hdev;
201562306a36Sopenharmony_ci	u64 export_addr;
201662306a36Sopenharmony_ci	int rc;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	hdev = ctx->hdev;
201962306a36Sopenharmony_ci	prop = &hdev->asic_prop;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	/* offset must be 0 in devices without virtual memory support */
202262306a36Sopenharmony_ci	if (!prop->dram_supports_virtual_memory && offset) {
202362306a36Sopenharmony_ci		dev_dbg(hdev->dev, "offset is not allowed in device without virtual memory\n");
202462306a36Sopenharmony_ci		return -EINVAL;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	export_addr = addr + offset;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL);
203062306a36Sopenharmony_ci	if (!hl_dmabuf)
203162306a36Sopenharmony_ci		return -ENOMEM;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	if (prop->dram_supports_virtual_memory) {
203462306a36Sopenharmony_ci		hnode = memhash_node_export_get(ctx, addr);
203562306a36Sopenharmony_ci		if (IS_ERR(hnode)) {
203662306a36Sopenharmony_ci			rc = PTR_ERR(hnode);
203762306a36Sopenharmony_ci			goto err_free_dmabuf_wrapper;
203862306a36Sopenharmony_ci		}
203962306a36Sopenharmony_ci		phys_pg_pack = get_phys_pg_pack_from_hash_node(hdev, hnode);
204062306a36Sopenharmony_ci		if (IS_ERR(phys_pg_pack)) {
204162306a36Sopenharmony_ci			rc = PTR_ERR(phys_pg_pack);
204262306a36Sopenharmony_ci			goto dec_memhash_export_cnt;
204362306a36Sopenharmony_ci		}
204462306a36Sopenharmony_ci		rc = validate_export_params(hdev, export_addr, size, offset, phys_pg_pack);
204562306a36Sopenharmony_ci		if (rc)
204662306a36Sopenharmony_ci			goto dec_memhash_export_cnt;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci		phys_pg_pack->exported_size = size;
204962306a36Sopenharmony_ci		hl_dmabuf->phys_pg_pack = phys_pg_pack;
205062306a36Sopenharmony_ci		hl_dmabuf->memhash_hnode = hnode;
205162306a36Sopenharmony_ci	} else {
205262306a36Sopenharmony_ci		rc = validate_export_params_no_mmu(hdev, export_addr, size);
205362306a36Sopenharmony_ci		if (rc)
205462306a36Sopenharmony_ci			goto err_free_dmabuf_wrapper;
205562306a36Sopenharmony_ci	}
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	hl_dmabuf->device_address = export_addr;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	rc = export_dmabuf(ctx, hl_dmabuf, size, flags, dmabuf_fd);
206062306a36Sopenharmony_ci	if (rc)
206162306a36Sopenharmony_ci		goto dec_memhash_export_cnt;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	return 0;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_cidec_memhash_export_cnt:
206662306a36Sopenharmony_ci	if (prop->dram_supports_virtual_memory)
206762306a36Sopenharmony_ci		memhash_node_export_put(ctx, hnode);
206862306a36Sopenharmony_cierr_free_dmabuf_wrapper:
206962306a36Sopenharmony_ci	kfree(hl_dmabuf);
207062306a36Sopenharmony_ci	return rc;
207162306a36Sopenharmony_ci}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic void ts_buff_release(struct hl_mmap_mem_buf *buf)
207462306a36Sopenharmony_ci{
207562306a36Sopenharmony_ci	struct hl_ts_buff *ts_buff = buf->private;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	vfree(ts_buff->kernel_buff_address);
207862306a36Sopenharmony_ci	vfree(ts_buff->user_buff_address);
207962306a36Sopenharmony_ci	kfree(ts_buff);
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_cistatic int hl_ts_mmap(struct hl_mmap_mem_buf *buf, struct vm_area_struct *vma, void *args)
208362306a36Sopenharmony_ci{
208462306a36Sopenharmony_ci	struct hl_ts_buff *ts_buff = buf->private;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE);
208762306a36Sopenharmony_ci	return remap_vmalloc_range(vma, ts_buff->user_buff_address, 0);
208862306a36Sopenharmony_ci}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_cistatic int hl_ts_alloc_buf(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args)
209162306a36Sopenharmony_ci{
209262306a36Sopenharmony_ci	struct hl_ts_buff *ts_buff = NULL;
209362306a36Sopenharmony_ci	u32 num_elements;
209462306a36Sopenharmony_ci	size_t size;
209562306a36Sopenharmony_ci	void *p;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	num_elements = *(u32 *)args;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	ts_buff = kzalloc(sizeof(*ts_buff), gfp);
210062306a36Sopenharmony_ci	if (!ts_buff)
210162306a36Sopenharmony_ci		return -ENOMEM;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	/* Allocate the user buffer */
210462306a36Sopenharmony_ci	size = num_elements * sizeof(u64);
210562306a36Sopenharmony_ci	p = vmalloc_user(size);
210662306a36Sopenharmony_ci	if (!p)
210762306a36Sopenharmony_ci		goto free_mem;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	ts_buff->user_buff_address = p;
211062306a36Sopenharmony_ci	buf->mappable_size = size;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	/* Allocate the internal kernel buffer */
211362306a36Sopenharmony_ci	size = num_elements * sizeof(struct hl_user_pending_interrupt);
211462306a36Sopenharmony_ci	p = vzalloc(size);
211562306a36Sopenharmony_ci	if (!p)
211662306a36Sopenharmony_ci		goto free_user_buff;
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	ts_buff->kernel_buff_address = p;
211962306a36Sopenharmony_ci	ts_buff->kernel_buff_size = size;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	buf->private = ts_buff;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	return 0;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_cifree_user_buff:
212662306a36Sopenharmony_ci	vfree(ts_buff->user_buff_address);
212762306a36Sopenharmony_cifree_mem:
212862306a36Sopenharmony_ci	kfree(ts_buff);
212962306a36Sopenharmony_ci	return -ENOMEM;
213062306a36Sopenharmony_ci}
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_cistatic struct hl_mmap_mem_buf_behavior hl_ts_behavior = {
213362306a36Sopenharmony_ci	.topic = "TS",
213462306a36Sopenharmony_ci	.mem_id = HL_MMAP_TYPE_TS_BUFF,
213562306a36Sopenharmony_ci	.mmap = hl_ts_mmap,
213662306a36Sopenharmony_ci	.alloc = hl_ts_alloc_buf,
213762306a36Sopenharmony_ci	.release = ts_buff_release,
213862306a36Sopenharmony_ci};
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/**
214162306a36Sopenharmony_ci * allocate_timestamps_buffers() - allocate timestamps buffers
214262306a36Sopenharmony_ci * This function will allocate ts buffer that will later on be mapped to the user
214362306a36Sopenharmony_ci * in order to be able to read the timestamp.
214462306a36Sopenharmony_ci * in addition it'll allocate an extra buffer for registration management.
214562306a36Sopenharmony_ci * since we cannot fail during registration for out-of-memory situation, so
214662306a36Sopenharmony_ci * we'll prepare a pool which will be used as user interrupt nodes and instead
214762306a36Sopenharmony_ci * of dynamically allocating nodes while registration we'll pick the node from
214862306a36Sopenharmony_ci * this pool. in addition it'll add node to the mapping hash which will be used
214962306a36Sopenharmony_ci * to map user ts buffer to the internal kernel ts buffer.
215062306a36Sopenharmony_ci * @hpriv: pointer to the private data of the fd
215162306a36Sopenharmony_ci * @args: ioctl input
215262306a36Sopenharmony_ci * @handle: user timestamp buffer handle as an output
215362306a36Sopenharmony_ci */
215462306a36Sopenharmony_cistatic int allocate_timestamps_buffers(struct hl_fpriv *hpriv, struct hl_mem_in *args, u64 *handle)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	struct hl_mem_mgr *mmg = &hpriv->mem_mgr;
215762306a36Sopenharmony_ci	struct hl_mmap_mem_buf *buf;
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	if (args->num_of_elements > TS_MAX_ELEMENTS_NUM) {
216062306a36Sopenharmony_ci		dev_err(mmg->dev, "Num of elements exceeds Max allowed number (0x%x > 0x%x)\n",
216162306a36Sopenharmony_ci				args->num_of_elements, TS_MAX_ELEMENTS_NUM);
216262306a36Sopenharmony_ci		return -EINVAL;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	buf = hl_mmap_mem_buf_alloc(mmg, &hl_ts_behavior, GFP_KERNEL, &args->num_of_elements);
216662306a36Sopenharmony_ci	if (!buf)
216762306a36Sopenharmony_ci		return -ENOMEM;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	*handle = buf->handle;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	return 0;
217262306a36Sopenharmony_ci}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ciint hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
217562306a36Sopenharmony_ci{
217662306a36Sopenharmony_ci	enum hl_device_status status;
217762306a36Sopenharmony_ci	union hl_mem_args *args = data;
217862306a36Sopenharmony_ci	struct hl_device *hdev = hpriv->hdev;
217962306a36Sopenharmony_ci	struct hl_ctx *ctx = hpriv->ctx;
218062306a36Sopenharmony_ci	u64 block_handle, device_addr = 0;
218162306a36Sopenharmony_ci	u32 handle = 0, block_size;
218262306a36Sopenharmony_ci	int rc, dmabuf_fd = -EBADF;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	if (!hl_device_operational(hdev, &status)) {
218562306a36Sopenharmony_ci		dev_dbg_ratelimited(hdev->dev,
218662306a36Sopenharmony_ci			"Device is %s. Can't execute MEMORY IOCTL\n",
218762306a36Sopenharmony_ci			hdev->status[status]);
218862306a36Sopenharmony_ci		return -EBUSY;
218962306a36Sopenharmony_ci	}
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	switch (args->in.op) {
219262306a36Sopenharmony_ci	case HL_MEM_OP_ALLOC:
219362306a36Sopenharmony_ci		if (args->in.alloc.mem_size == 0) {
219462306a36Sopenharmony_ci			dev_err(hdev->dev,
219562306a36Sopenharmony_ci				"alloc size must be larger than 0\n");
219662306a36Sopenharmony_ci			rc = -EINVAL;
219762306a36Sopenharmony_ci			goto out;
219862306a36Sopenharmony_ci		}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci		/* If DRAM does not support virtual memory the driver won't
220162306a36Sopenharmony_ci		 * handle the allocation/freeing of that memory. However, for
220262306a36Sopenharmony_ci		 * system administration/monitoring purposes, the driver will
220362306a36Sopenharmony_ci		 * keep track of the amount of DRAM memory that is allocated
220462306a36Sopenharmony_ci		 * and freed by the user. Because this code totally relies on
220562306a36Sopenharmony_ci		 * the user's input, the driver can't ensure the validity
220662306a36Sopenharmony_ci		 * of this accounting.
220762306a36Sopenharmony_ci		 */
220862306a36Sopenharmony_ci		if (!hdev->asic_prop.dram_supports_virtual_memory) {
220962306a36Sopenharmony_ci			atomic64_add(args->in.alloc.mem_size,
221062306a36Sopenharmony_ci					&ctx->dram_phys_mem);
221162306a36Sopenharmony_ci			atomic64_add(args->in.alloc.mem_size,
221262306a36Sopenharmony_ci					&hdev->dram_used_mem);
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci			dev_dbg(hdev->dev, "DRAM alloc is not supported\n");
221562306a36Sopenharmony_ci			rc = 0;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci			memset(args, 0, sizeof(*args));
221862306a36Sopenharmony_ci			args->out.handle = 0;
221962306a36Sopenharmony_ci			goto out;
222062306a36Sopenharmony_ci		}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		rc = alloc_device_memory(ctx, &args->in, &handle);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci		memset(args, 0, sizeof(*args));
222562306a36Sopenharmony_ci		args->out.handle = (__u64) handle;
222662306a36Sopenharmony_ci		break;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	case HL_MEM_OP_FREE:
222962306a36Sopenharmony_ci		/* If DRAM does not support virtual memory the driver won't
223062306a36Sopenharmony_ci		 * handle the allocation/freeing of that memory. However, for
223162306a36Sopenharmony_ci		 * system administration/monitoring purposes, the driver will
223262306a36Sopenharmony_ci		 * keep track of the amount of DRAM memory that is allocated
223362306a36Sopenharmony_ci		 * and freed by the user. Because this code totally relies on
223462306a36Sopenharmony_ci		 * the user's input, the driver can't ensure the validity
223562306a36Sopenharmony_ci		 * of this accounting.
223662306a36Sopenharmony_ci		 */
223762306a36Sopenharmony_ci		if (!hdev->asic_prop.dram_supports_virtual_memory) {
223862306a36Sopenharmony_ci			atomic64_sub(args->in.alloc.mem_size,
223962306a36Sopenharmony_ci					&ctx->dram_phys_mem);
224062306a36Sopenharmony_ci			atomic64_sub(args->in.alloc.mem_size,
224162306a36Sopenharmony_ci					&hdev->dram_used_mem);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci			dev_dbg(hdev->dev, "DRAM alloc is not supported\n");
224462306a36Sopenharmony_ci			rc = 0;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci			goto out;
224762306a36Sopenharmony_ci		}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci		rc = free_device_memory(ctx, &args->in);
225062306a36Sopenharmony_ci		break;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	case HL_MEM_OP_MAP:
225362306a36Sopenharmony_ci		rc = map_device_va(ctx, &args->in, &device_addr);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci		memset(args, 0, sizeof(*args));
225662306a36Sopenharmony_ci		args->out.device_virt_addr = device_addr;
225762306a36Sopenharmony_ci		break;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	case HL_MEM_OP_UNMAP:
226062306a36Sopenharmony_ci		rc = unmap_device_va(ctx, &args->in, false);
226162306a36Sopenharmony_ci		break;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	case HL_MEM_OP_MAP_BLOCK:
226462306a36Sopenharmony_ci		rc = map_block(hdev, args->in.map_block.block_addr,
226562306a36Sopenharmony_ci				&block_handle, &block_size);
226662306a36Sopenharmony_ci		args->out.block_handle = block_handle;
226762306a36Sopenharmony_ci		args->out.block_size = block_size;
226862306a36Sopenharmony_ci		break;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	case HL_MEM_OP_EXPORT_DMABUF_FD:
227162306a36Sopenharmony_ci		rc = export_dmabuf_from_addr(ctx,
227262306a36Sopenharmony_ci				args->in.export_dmabuf_fd.addr,
227362306a36Sopenharmony_ci				args->in.export_dmabuf_fd.mem_size,
227462306a36Sopenharmony_ci				args->in.export_dmabuf_fd.offset,
227562306a36Sopenharmony_ci				args->in.flags,
227662306a36Sopenharmony_ci				&dmabuf_fd);
227762306a36Sopenharmony_ci		memset(args, 0, sizeof(*args));
227862306a36Sopenharmony_ci		args->out.fd = dmabuf_fd;
227962306a36Sopenharmony_ci		break;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	case HL_MEM_OP_TS_ALLOC:
228262306a36Sopenharmony_ci		rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
228362306a36Sopenharmony_ci		break;
228462306a36Sopenharmony_ci	default:
228562306a36Sopenharmony_ci		dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
228662306a36Sopenharmony_ci		rc = -EINVAL;
228762306a36Sopenharmony_ci		break;
228862306a36Sopenharmony_ci	}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ciout:
229162306a36Sopenharmony_ci	return rc;
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_cistatic int get_user_memory(struct hl_device *hdev, u64 addr, u64 size,
229562306a36Sopenharmony_ci				u32 npages, u64 start, u32 offset,
229662306a36Sopenharmony_ci				struct hl_userptr *userptr)
229762306a36Sopenharmony_ci{
229862306a36Sopenharmony_ci	int rc;
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (!access_ok((void __user *) (uintptr_t) addr, size)) {
230162306a36Sopenharmony_ci		dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr);
230262306a36Sopenharmony_ci		return -EFAULT;
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	userptr->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
230662306a36Sopenharmony_ci	if (!userptr->pages)
230762306a36Sopenharmony_ci		return -ENOMEM;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	rc = pin_user_pages_fast(start, npages, FOLL_WRITE | FOLL_LONGTERM,
231062306a36Sopenharmony_ci				 userptr->pages);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (rc != npages) {
231362306a36Sopenharmony_ci		dev_err(hdev->dev,
231462306a36Sopenharmony_ci			"Failed (%d) to pin host memory with user ptr 0x%llx, size 0x%llx, npages %d\n",
231562306a36Sopenharmony_ci			rc, addr, size, npages);
231662306a36Sopenharmony_ci		if (rc < 0)
231762306a36Sopenharmony_ci			goto destroy_pages;
231862306a36Sopenharmony_ci		npages = rc;
231962306a36Sopenharmony_ci		rc = -EFAULT;
232062306a36Sopenharmony_ci		goto put_pages;
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci	userptr->npages = npages;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	rc = sg_alloc_table_from_pages(userptr->sgt,
232562306a36Sopenharmony_ci				       userptr->pages,
232662306a36Sopenharmony_ci				       npages, offset, size, GFP_KERNEL);
232762306a36Sopenharmony_ci	if (rc < 0) {
232862306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to create SG table from pages\n");
232962306a36Sopenharmony_ci		goto put_pages;
233062306a36Sopenharmony_ci	}
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	return 0;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ciput_pages:
233562306a36Sopenharmony_ci	unpin_user_pages(userptr->pages, npages);
233662306a36Sopenharmony_cidestroy_pages:
233762306a36Sopenharmony_ci	kvfree(userptr->pages);
233862306a36Sopenharmony_ci	return rc;
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci/**
234262306a36Sopenharmony_ci * hl_pin_host_memory() - pins a chunk of host memory.
234362306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
234462306a36Sopenharmony_ci * @addr: the host virtual address of the memory area.
234562306a36Sopenharmony_ci * @size: the size of the memory area.
234662306a36Sopenharmony_ci * @userptr: pointer to hl_userptr structure.
234762306a36Sopenharmony_ci *
234862306a36Sopenharmony_ci * This function does the following:
234962306a36Sopenharmony_ci * - Pins the physical pages.
235062306a36Sopenharmony_ci * - Create an SG list from those pages.
235162306a36Sopenharmony_ci */
235262306a36Sopenharmony_ciint hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
235362306a36Sopenharmony_ci					struct hl_userptr *userptr)
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci	u64 start, end;
235662306a36Sopenharmony_ci	u32 npages, offset;
235762306a36Sopenharmony_ci	int rc;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	if (!size) {
236062306a36Sopenharmony_ci		dev_err(hdev->dev, "size to pin is invalid - %llu\n", size);
236162306a36Sopenharmony_ci		return -EINVAL;
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	/*
236562306a36Sopenharmony_ci	 * If the combination of the address and size requested for this memory
236662306a36Sopenharmony_ci	 * region causes an integer overflow, return error.
236762306a36Sopenharmony_ci	 */
236862306a36Sopenharmony_ci	if (((addr + size) < addr) ||
236962306a36Sopenharmony_ci			PAGE_ALIGN(addr + size) < (addr + size)) {
237062306a36Sopenharmony_ci		dev_err(hdev->dev,
237162306a36Sopenharmony_ci			"user pointer 0x%llx + %llu causes integer overflow\n",
237262306a36Sopenharmony_ci			addr, size);
237362306a36Sopenharmony_ci		return -EINVAL;
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	userptr->pid = current->pid;
237762306a36Sopenharmony_ci	userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_KERNEL);
237862306a36Sopenharmony_ci	if (!userptr->sgt)
237962306a36Sopenharmony_ci		return -ENOMEM;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	start = addr & PAGE_MASK;
238262306a36Sopenharmony_ci	offset = addr & ~PAGE_MASK;
238362306a36Sopenharmony_ci	end = PAGE_ALIGN(addr + size);
238462306a36Sopenharmony_ci	npages = (end - start) >> PAGE_SHIFT;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	userptr->size = size;
238762306a36Sopenharmony_ci	userptr->addr = addr;
238862306a36Sopenharmony_ci	userptr->dma_mapped = false;
238962306a36Sopenharmony_ci	INIT_LIST_HEAD(&userptr->job_node);
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	rc = get_user_memory(hdev, addr, size, npages, start, offset,
239262306a36Sopenharmony_ci				userptr);
239362306a36Sopenharmony_ci	if (rc) {
239462306a36Sopenharmony_ci		dev_err(hdev->dev,
239562306a36Sopenharmony_ci			"failed to get user memory for address 0x%llx\n",
239662306a36Sopenharmony_ci			addr);
239762306a36Sopenharmony_ci		goto free_sgt;
239862306a36Sopenharmony_ci	}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	hl_debugfs_add_userptr(hdev, userptr);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	return 0;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_cifree_sgt:
240562306a36Sopenharmony_ci	kfree(userptr->sgt);
240662306a36Sopenharmony_ci	return rc;
240762306a36Sopenharmony_ci}
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci/*
241062306a36Sopenharmony_ci * hl_unpin_host_memory - unpins a chunk of host memory.
241162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure
241262306a36Sopenharmony_ci * @userptr: pointer to hl_userptr structure
241362306a36Sopenharmony_ci *
241462306a36Sopenharmony_ci * This function does the following:
241562306a36Sopenharmony_ci * - Unpins the physical pages related to the host memory
241662306a36Sopenharmony_ci * - Free the SG list
241762306a36Sopenharmony_ci */
241862306a36Sopenharmony_civoid hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	hl_debugfs_remove_userptr(hdev, userptr);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	if (userptr->dma_mapped)
242362306a36Sopenharmony_ci		hdev->asic_funcs->hl_dma_unmap_sgtable(hdev, userptr->sgt, userptr->dir);
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	unpin_user_pages_dirty_lock(userptr->pages, userptr->npages, true);
242662306a36Sopenharmony_ci	kvfree(userptr->pages);
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	list_del(&userptr->job_node);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	sg_free_table(userptr->sgt);
243162306a36Sopenharmony_ci	kfree(userptr->sgt);
243262306a36Sopenharmony_ci}
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci/**
243562306a36Sopenharmony_ci * hl_userptr_delete_list() - clear userptr list.
243662306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
243762306a36Sopenharmony_ci * @userptr_list: pointer to the list to clear.
243862306a36Sopenharmony_ci *
243962306a36Sopenharmony_ci * This function does the following:
244062306a36Sopenharmony_ci * - Iterates over the list and unpins the host memory and frees the userptr
244162306a36Sopenharmony_ci *   structure.
244262306a36Sopenharmony_ci */
244362306a36Sopenharmony_civoid hl_userptr_delete_list(struct hl_device *hdev,
244462306a36Sopenharmony_ci				struct list_head *userptr_list)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	struct hl_userptr *userptr, *tmp;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	list_for_each_entry_safe(userptr, tmp, userptr_list, job_node) {
244962306a36Sopenharmony_ci		hl_unpin_host_memory(hdev, userptr);
245062306a36Sopenharmony_ci		kfree(userptr);
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	INIT_LIST_HEAD(userptr_list);
245462306a36Sopenharmony_ci}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci/**
245762306a36Sopenharmony_ci * hl_userptr_is_pinned() - returns whether the given userptr is pinned.
245862306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
245962306a36Sopenharmony_ci * @addr: user address to check.
246062306a36Sopenharmony_ci * @size: user block size to check.
246162306a36Sopenharmony_ci * @userptr_list: pointer to the list to clear.
246262306a36Sopenharmony_ci * @userptr: pointer to userptr to check.
246362306a36Sopenharmony_ci *
246462306a36Sopenharmony_ci * This function does the following:
246562306a36Sopenharmony_ci * - Iterates over the list and checks if the given userptr is in it, means is
246662306a36Sopenharmony_ci *   pinned. If so, returns true, otherwise returns false.
246762306a36Sopenharmony_ci */
246862306a36Sopenharmony_cibool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr,
246962306a36Sopenharmony_ci				u32 size, struct list_head *userptr_list,
247062306a36Sopenharmony_ci				struct hl_userptr **userptr)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	list_for_each_entry((*userptr), userptr_list, job_node) {
247362306a36Sopenharmony_ci		if ((addr == (*userptr)->addr) && (size == (*userptr)->size))
247462306a36Sopenharmony_ci			return true;
247562306a36Sopenharmony_ci	}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	return false;
247862306a36Sopenharmony_ci}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci/**
248162306a36Sopenharmony_ci * va_range_init() - initialize virtual addresses range.
248262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
248362306a36Sopenharmony_ci * @va_ranges: pointer to va_ranges array.
248462306a36Sopenharmony_ci * @range_type: virtual address range type.
248562306a36Sopenharmony_ci * @start: range start address, inclusive.
248662306a36Sopenharmony_ci * @end: range end address, inclusive.
248762306a36Sopenharmony_ci * @page_size: page size for this va_range.
248862306a36Sopenharmony_ci *
248962306a36Sopenharmony_ci * This function does the following:
249062306a36Sopenharmony_ci * - Initializes the virtual addresses list of the given range with the given
249162306a36Sopenharmony_ci *   addresses.
249262306a36Sopenharmony_ci */
249362306a36Sopenharmony_cistatic int va_range_init(struct hl_device *hdev, struct hl_va_range **va_ranges,
249462306a36Sopenharmony_ci				enum hl_va_range_type range_type, u64 start,
249562306a36Sopenharmony_ci				u64 end, u32 page_size)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	struct hl_va_range *va_range = va_ranges[range_type];
249862306a36Sopenharmony_ci	int rc;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	INIT_LIST_HEAD(&va_range->list);
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	/*
250362306a36Sopenharmony_ci	 * PAGE_SIZE alignment
250462306a36Sopenharmony_ci	 * it is the caller's responsibility to align the addresses if the
250562306a36Sopenharmony_ci	 * page size is not a power of 2
250662306a36Sopenharmony_ci	 */
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	if (is_power_of_2(page_size)) {
250962306a36Sopenharmony_ci		start = round_up(start, page_size);
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci		/*
251262306a36Sopenharmony_ci		 * The end of the range is inclusive, hence we need to align it
251362306a36Sopenharmony_ci		 * to the end of the last full page in the range. For example if
251462306a36Sopenharmony_ci		 * end = 0x3ff5 with page size 0x1000, we need to align it to
251562306a36Sopenharmony_ci		 * 0x2fff. The remaining 0xff5 bytes do not form a full page.
251662306a36Sopenharmony_ci		 */
251762306a36Sopenharmony_ci		end = round_down(end + 1, page_size) - 1;
251862306a36Sopenharmony_ci	}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	if (start >= end) {
252162306a36Sopenharmony_ci		dev_err(hdev->dev, "too small vm range for va list\n");
252262306a36Sopenharmony_ci		return -EFAULT;
252362306a36Sopenharmony_ci	}
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	rc = add_va_block(hdev, va_range, start, end);
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	if (rc) {
252862306a36Sopenharmony_ci		dev_err(hdev->dev, "Failed to init host va list\n");
252962306a36Sopenharmony_ci		return rc;
253062306a36Sopenharmony_ci	}
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	va_range->start_addr = start;
253362306a36Sopenharmony_ci	va_range->end_addr = end;
253462306a36Sopenharmony_ci	va_range->page_size = page_size;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	return 0;
253762306a36Sopenharmony_ci}
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci/**
254062306a36Sopenharmony_ci * va_range_fini() - clear a virtual addresses range.
254162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs structure.
254262306a36Sopenharmony_ci * @va_range: pointer to virtual addresses range.
254362306a36Sopenharmony_ci *
254462306a36Sopenharmony_ci * This function does the following:
254562306a36Sopenharmony_ci * - Frees the virtual addresses block list and its lock.
254662306a36Sopenharmony_ci */
254762306a36Sopenharmony_cistatic void va_range_fini(struct hl_device *hdev, struct hl_va_range *va_range)
254862306a36Sopenharmony_ci{
254962306a36Sopenharmony_ci	mutex_lock(&va_range->lock);
255062306a36Sopenharmony_ci	clear_va_list_locked(hdev, &va_range->list);
255162306a36Sopenharmony_ci	mutex_unlock(&va_range->lock);
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	mutex_destroy(&va_range->lock);
255462306a36Sopenharmony_ci	kfree(va_range);
255562306a36Sopenharmony_ci}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci/**
255862306a36Sopenharmony_ci * vm_ctx_init_with_ranges() - initialize virtual memory for context.
255962306a36Sopenharmony_ci * @ctx: pointer to the habanalabs context structure.
256062306a36Sopenharmony_ci * @host_range_start: host virtual addresses range start.
256162306a36Sopenharmony_ci * @host_range_end: host virtual addresses range end.
256262306a36Sopenharmony_ci * @host_page_size: host page size.
256362306a36Sopenharmony_ci * @host_huge_range_start: host virtual addresses range start for memory
256462306a36Sopenharmony_ci *                         allocated with huge pages.
256562306a36Sopenharmony_ci * @host_huge_range_end: host virtual addresses range end for memory allocated
256662306a36Sopenharmony_ci *                        with huge pages.
256762306a36Sopenharmony_ci * @host_huge_page_size: host huge page size.
256862306a36Sopenharmony_ci * @dram_range_start: dram virtual addresses range start.
256962306a36Sopenharmony_ci * @dram_range_end: dram virtual addresses range end.
257062306a36Sopenharmony_ci * @dram_page_size: dram page size.
257162306a36Sopenharmony_ci *
257262306a36Sopenharmony_ci * This function initializes the following:
257362306a36Sopenharmony_ci * - MMU for context.
257462306a36Sopenharmony_ci * - Virtual address to area descriptor hashtable.
257562306a36Sopenharmony_ci * - Virtual block list of available virtual memory.
257662306a36Sopenharmony_ci */
257762306a36Sopenharmony_cistatic int vm_ctx_init_with_ranges(struct hl_ctx *ctx,
257862306a36Sopenharmony_ci					u64 host_range_start,
257962306a36Sopenharmony_ci					u64 host_range_end,
258062306a36Sopenharmony_ci					u32 host_page_size,
258162306a36Sopenharmony_ci					u64 host_huge_range_start,
258262306a36Sopenharmony_ci					u64 host_huge_range_end,
258362306a36Sopenharmony_ci					u32 host_huge_page_size,
258462306a36Sopenharmony_ci					u64 dram_range_start,
258562306a36Sopenharmony_ci					u64 dram_range_end,
258662306a36Sopenharmony_ci					u32 dram_page_size)
258762306a36Sopenharmony_ci{
258862306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
258962306a36Sopenharmony_ci	int i, rc;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX ; i++) {
259262306a36Sopenharmony_ci		ctx->va_range[i] =
259362306a36Sopenharmony_ci			kzalloc(sizeof(struct hl_va_range), GFP_KERNEL);
259462306a36Sopenharmony_ci		if (!ctx->va_range[i]) {
259562306a36Sopenharmony_ci			rc = -ENOMEM;
259662306a36Sopenharmony_ci			goto free_va_range;
259762306a36Sopenharmony_ci		}
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	rc = hl_mmu_ctx_init(ctx);
260162306a36Sopenharmony_ci	if (rc) {
260262306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to init context %d\n", ctx->asid);
260362306a36Sopenharmony_ci		goto free_va_range;
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	mutex_init(&ctx->mem_hash_lock);
260762306a36Sopenharmony_ci	hash_init(ctx->mem_hash);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	rc = va_range_init(hdev, ctx->va_range, HL_VA_RANGE_TYPE_HOST,
261262306a36Sopenharmony_ci			host_range_start, host_range_end, host_page_size);
261362306a36Sopenharmony_ci	if (rc) {
261462306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to init host vm range\n");
261562306a36Sopenharmony_ci		goto mmu_ctx_fini;
261662306a36Sopenharmony_ci	}
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	if (hdev->pmmu_huge_range) {
261962306a36Sopenharmony_ci		mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci		rc = va_range_init(hdev,
262262306a36Sopenharmony_ci			ctx->va_range, HL_VA_RANGE_TYPE_HOST_HUGE,
262362306a36Sopenharmony_ci			host_huge_range_start, host_huge_range_end,
262462306a36Sopenharmony_ci			host_huge_page_size);
262562306a36Sopenharmony_ci		if (rc) {
262662306a36Sopenharmony_ci			dev_err(hdev->dev,
262762306a36Sopenharmony_ci				"failed to init host huge vm range\n");
262862306a36Sopenharmony_ci			goto clear_host_va_range;
262962306a36Sopenharmony_ci		}
263062306a36Sopenharmony_ci	} else {
263162306a36Sopenharmony_ci		kfree(ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]);
263262306a36Sopenharmony_ci		ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE] =
263362306a36Sopenharmony_ci				ctx->va_range[HL_VA_RANGE_TYPE_HOST];
263462306a36Sopenharmony_ci	}
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_DRAM]->lock);
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	rc = va_range_init(hdev, ctx->va_range, HL_VA_RANGE_TYPE_DRAM,
263962306a36Sopenharmony_ci			dram_range_start, dram_range_end, dram_page_size);
264062306a36Sopenharmony_ci	if (rc) {
264162306a36Sopenharmony_ci		dev_err(hdev->dev, "failed to init dram vm range\n");
264262306a36Sopenharmony_ci		goto clear_host_huge_va_range;
264362306a36Sopenharmony_ci	}
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	hl_debugfs_add_ctx_mem_hash(hdev, ctx);
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	return 0;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ciclear_host_huge_va_range:
265062306a36Sopenharmony_ci	mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_DRAM]->lock);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	if (hdev->pmmu_huge_range) {
265362306a36Sopenharmony_ci		mutex_lock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
265462306a36Sopenharmony_ci		clear_va_list_locked(hdev,
265562306a36Sopenharmony_ci			&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->list);
265662306a36Sopenharmony_ci		mutex_unlock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
265762306a36Sopenharmony_ci	}
265862306a36Sopenharmony_ciclear_host_va_range:
265962306a36Sopenharmony_ci	if (hdev->pmmu_huge_range)
266062306a36Sopenharmony_ci		mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
266162306a36Sopenharmony_ci	mutex_lock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
266262306a36Sopenharmony_ci	clear_va_list_locked(hdev, &ctx->va_range[HL_VA_RANGE_TYPE_HOST]->list);
266362306a36Sopenharmony_ci	mutex_unlock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
266462306a36Sopenharmony_cimmu_ctx_fini:
266562306a36Sopenharmony_ci	mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
266662306a36Sopenharmony_ci	mutex_destroy(&ctx->mem_hash_lock);
266762306a36Sopenharmony_ci	hl_mmu_ctx_fini(ctx);
266862306a36Sopenharmony_cifree_va_range:
266962306a36Sopenharmony_ci	for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX ; i++)
267062306a36Sopenharmony_ci		kfree(ctx->va_range[i]);
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	return rc;
267362306a36Sopenharmony_ci}
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ciint hl_vm_ctx_init(struct hl_ctx *ctx)
267662306a36Sopenharmony_ci{
267762306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
267862306a36Sopenharmony_ci	u64 host_range_start, host_range_end, host_huge_range_start,
267962306a36Sopenharmony_ci		host_huge_range_end, dram_range_start, dram_range_end;
268062306a36Sopenharmony_ci	u32 host_page_size, host_huge_page_size, dram_page_size;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	atomic64_set(&ctx->dram_phys_mem, 0);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	/*
268562306a36Sopenharmony_ci	 *   In case of DRAM mapping, the returned address is the physical
268662306a36Sopenharmony_ci	 *   address of the memory related to the given handle.
268762306a36Sopenharmony_ci	 */
268862306a36Sopenharmony_ci	if (ctx->hdev->mmu_disable)
268962306a36Sopenharmony_ci		return 0;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	dram_range_start = prop->dmmu.start_addr;
269262306a36Sopenharmony_ci	dram_range_end = prop->dmmu.end_addr - 1;
269362306a36Sopenharmony_ci	dram_page_size = prop->dram_page_size ?
269462306a36Sopenharmony_ci				prop->dram_page_size : prop->dmmu.page_size;
269562306a36Sopenharmony_ci	host_range_start = prop->pmmu.start_addr;
269662306a36Sopenharmony_ci	host_range_end = prop->pmmu.end_addr - 1;
269762306a36Sopenharmony_ci	host_page_size = prop->pmmu.page_size;
269862306a36Sopenharmony_ci	host_huge_range_start = prop->pmmu_huge.start_addr;
269962306a36Sopenharmony_ci	host_huge_range_end = prop->pmmu_huge.end_addr - 1;
270062306a36Sopenharmony_ci	host_huge_page_size = prop->pmmu_huge.page_size;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	return vm_ctx_init_with_ranges(ctx, host_range_start, host_range_end,
270362306a36Sopenharmony_ci			host_page_size, host_huge_range_start,
270462306a36Sopenharmony_ci			host_huge_range_end, host_huge_page_size,
270562306a36Sopenharmony_ci			dram_range_start, dram_range_end, dram_page_size);
270662306a36Sopenharmony_ci}
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci/**
270962306a36Sopenharmony_ci * hl_vm_ctx_fini() - virtual memory teardown of context.
271062306a36Sopenharmony_ci * @ctx: pointer to the habanalabs context structure.
271162306a36Sopenharmony_ci *
271262306a36Sopenharmony_ci * This function perform teardown the following:
271362306a36Sopenharmony_ci * - Virtual block list of available virtual memory.
271462306a36Sopenharmony_ci * - Virtual address to area descriptor hashtable.
271562306a36Sopenharmony_ci * - MMU for context.
271662306a36Sopenharmony_ci *
271762306a36Sopenharmony_ci * In addition this function does the following:
271862306a36Sopenharmony_ci * - Unmaps the existing hashtable nodes if the hashtable is not empty. The
271962306a36Sopenharmony_ci *   hashtable should be empty as no valid mappings should exist at this
272062306a36Sopenharmony_ci *   point.
272162306a36Sopenharmony_ci * - Frees any existing physical page list from the idr which relates to the
272262306a36Sopenharmony_ci *   current context asid.
272362306a36Sopenharmony_ci * - This function checks the virtual block list for correctness. At this point
272462306a36Sopenharmony_ci *   the list should contain one element which describes the whole virtual
272562306a36Sopenharmony_ci *   memory range of the context. Otherwise, a warning is printed.
272662306a36Sopenharmony_ci */
272762306a36Sopenharmony_civoid hl_vm_ctx_fini(struct hl_ctx *ctx)
272862306a36Sopenharmony_ci{
272962306a36Sopenharmony_ci	struct hl_vm_phys_pg_pack *phys_pg_list, *tmp_phys_node;
273062306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
273162306a36Sopenharmony_ci	struct hl_vm_hash_node *hnode;
273262306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
273362306a36Sopenharmony_ci	struct hlist_node *tmp_node;
273462306a36Sopenharmony_ci	struct list_head free_list;
273562306a36Sopenharmony_ci	struct hl_mem_in args;
273662306a36Sopenharmony_ci	int i;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	if (hdev->mmu_disable)
273962306a36Sopenharmony_ci		return;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	hl_debugfs_remove_ctx_mem_hash(hdev, ctx);
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	/*
274462306a36Sopenharmony_ci	 * Clearly something went wrong on hard reset so no point in printing
274562306a36Sopenharmony_ci	 * another side effect error
274662306a36Sopenharmony_ci	 */
274762306a36Sopenharmony_ci	if (!hdev->reset_info.hard_reset_pending && !hash_empty(ctx->mem_hash))
274862306a36Sopenharmony_ci		dev_dbg(hdev->dev,
274962306a36Sopenharmony_ci			"user released device without removing its memory mappings\n");
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) {
275262306a36Sopenharmony_ci		dev_dbg(hdev->dev,
275362306a36Sopenharmony_ci			"hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n",
275462306a36Sopenharmony_ci			hnode->vaddr, ctx->asid);
275562306a36Sopenharmony_ci		args.unmap.device_virt_addr = hnode->vaddr;
275662306a36Sopenharmony_ci		unmap_device_va(ctx, &args, true);
275762306a36Sopenharmony_ci	}
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	mutex_lock(&hdev->mmu_lock);
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	/* invalidate the cache once after the unmapping loop */
276262306a36Sopenharmony_ci	hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
276362306a36Sopenharmony_ci	hl_mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK);
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	mutex_unlock(&hdev->mmu_lock);
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	INIT_LIST_HEAD(&free_list);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	spin_lock(&vm->idr_lock);
277062306a36Sopenharmony_ci	idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i)
277162306a36Sopenharmony_ci		if (phys_pg_list->asid == ctx->asid) {
277262306a36Sopenharmony_ci			dev_dbg(hdev->dev,
277362306a36Sopenharmony_ci				"page list 0x%px of asid %d is still alive\n",
277462306a36Sopenharmony_ci				phys_pg_list, ctx->asid);
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci			atomic64_sub(phys_pg_list->total_size, &hdev->dram_used_mem);
277762306a36Sopenharmony_ci			idr_remove(&vm->phys_pg_pack_handles, i);
277862306a36Sopenharmony_ci			list_add(&phys_pg_list->node, &free_list);
277962306a36Sopenharmony_ci		}
278062306a36Sopenharmony_ci	spin_unlock(&vm->idr_lock);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	list_for_each_entry_safe(phys_pg_list, tmp_phys_node, &free_list, node)
278362306a36Sopenharmony_ci		free_phys_pg_pack(hdev, phys_pg_list);
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_DRAM]);
278662306a36Sopenharmony_ci	va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST]);
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	if (hdev->pmmu_huge_range)
278962306a36Sopenharmony_ci		va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	mutex_destroy(&ctx->mem_hash_lock);
279262306a36Sopenharmony_ci	hl_mmu_ctx_fini(ctx);
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	/* In this case we need to clear the global accounting of DRAM usage
279562306a36Sopenharmony_ci	 * because the user notifies us on allocations. If the user is no more,
279662306a36Sopenharmony_ci	 * all DRAM is available
279762306a36Sopenharmony_ci	 */
279862306a36Sopenharmony_ci	if (ctx->asid != HL_KERNEL_ASID_ID &&
279962306a36Sopenharmony_ci			!hdev->asic_prop.dram_supports_virtual_memory)
280062306a36Sopenharmony_ci		atomic64_set(&hdev->dram_used_mem, 0);
280162306a36Sopenharmony_ci}
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci/**
280462306a36Sopenharmony_ci * hl_vm_init() - initialize virtual memory module.
280562306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
280662306a36Sopenharmony_ci *
280762306a36Sopenharmony_ci * This function initializes the following:
280862306a36Sopenharmony_ci * - MMU module.
280962306a36Sopenharmony_ci * - DRAM physical pages pool of 2MB.
281062306a36Sopenharmony_ci * - Idr for device memory allocation handles.
281162306a36Sopenharmony_ci */
281262306a36Sopenharmony_ciint hl_vm_init(struct hl_device *hdev)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
281562306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
281662306a36Sopenharmony_ci	int rc;
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	if (is_power_of_2(prop->dram_page_size))
281962306a36Sopenharmony_ci		vm->dram_pg_pool =
282062306a36Sopenharmony_ci			gen_pool_create(__ffs(prop->dram_page_size), -1);
282162306a36Sopenharmony_ci	else
282262306a36Sopenharmony_ci		vm->dram_pg_pool =
282362306a36Sopenharmony_ci			gen_pool_create(__ffs(DRAM_POOL_PAGE_SIZE), -1);
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	if (!vm->dram_pg_pool) {
282662306a36Sopenharmony_ci		dev_err(hdev->dev, "Failed to create dram page pool\n");
282762306a36Sopenharmony_ci		return -ENOMEM;
282862306a36Sopenharmony_ci	}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	kref_init(&vm->dram_pg_pool_refcount);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	rc = gen_pool_add(vm->dram_pg_pool, prop->dram_user_base_address,
283362306a36Sopenharmony_ci			prop->dram_end_address - prop->dram_user_base_address,
283462306a36Sopenharmony_ci			-1);
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci	if (rc) {
283762306a36Sopenharmony_ci		dev_err(hdev->dev,
283862306a36Sopenharmony_ci			"Failed to add memory to dram page pool %d\n", rc);
283962306a36Sopenharmony_ci		goto pool_add_err;
284062306a36Sopenharmony_ci	}
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_ci	spin_lock_init(&vm->idr_lock);
284362306a36Sopenharmony_ci	idr_init(&vm->phys_pg_pack_handles);
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	atomic64_set(&hdev->dram_used_mem, 0);
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	vm->init_done = true;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	return 0;
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_cipool_add_err:
285262306a36Sopenharmony_ci	gen_pool_destroy(vm->dram_pg_pool);
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	return rc;
285562306a36Sopenharmony_ci}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci/**
285862306a36Sopenharmony_ci * hl_vm_fini() - virtual memory module teardown.
285962306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure.
286062306a36Sopenharmony_ci *
286162306a36Sopenharmony_ci * This function perform teardown to the following:
286262306a36Sopenharmony_ci * - Idr for device memory allocation handles.
286362306a36Sopenharmony_ci * - DRAM physical pages pool of 2MB.
286462306a36Sopenharmony_ci * - MMU module.
286562306a36Sopenharmony_ci */
286662306a36Sopenharmony_civoid hl_vm_fini(struct hl_device *hdev)
286762306a36Sopenharmony_ci{
286862306a36Sopenharmony_ci	struct hl_vm *vm = &hdev->vm;
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	if (!vm->init_done)
287162306a36Sopenharmony_ci		return;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	/*
287462306a36Sopenharmony_ci	 * At this point all the contexts should be freed and hence no DRAM
287562306a36Sopenharmony_ci	 * memory should be in use. Hence the DRAM pool should be freed here.
287662306a36Sopenharmony_ci	 */
287762306a36Sopenharmony_ci	if (kref_put(&vm->dram_pg_pool_refcount, dram_pg_pool_do_release) != 1)
287862306a36Sopenharmony_ci		dev_warn(hdev->dev, "dram_pg_pool was not destroyed on %s\n",
287962306a36Sopenharmony_ci				__func__);
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	vm->init_done = false;
288262306a36Sopenharmony_ci}
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci/**
288562306a36Sopenharmony_ci * hl_hw_block_mem_init() - HW block memory initialization.
288662306a36Sopenharmony_ci * @ctx: pointer to the habanalabs context structure.
288762306a36Sopenharmony_ci *
288862306a36Sopenharmony_ci * This function initializes the HW block virtual mapped addresses list and
288962306a36Sopenharmony_ci * it's lock.
289062306a36Sopenharmony_ci */
289162306a36Sopenharmony_civoid hl_hw_block_mem_init(struct hl_ctx *ctx)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	mutex_init(&ctx->hw_block_list_lock);
289462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->hw_block_mem_list);
289562306a36Sopenharmony_ci}
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci/**
289862306a36Sopenharmony_ci * hl_hw_block_mem_fini() - HW block memory teardown.
289962306a36Sopenharmony_ci * @ctx: pointer to the habanalabs context structure.
290062306a36Sopenharmony_ci *
290162306a36Sopenharmony_ci * This function clears the HW block virtual mapped addresses list and destroys
290262306a36Sopenharmony_ci * it's lock.
290362306a36Sopenharmony_ci */
290462306a36Sopenharmony_civoid hl_hw_block_mem_fini(struct hl_ctx *ctx)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	struct hl_vm_hw_block_list_node *lnode, *tmp;
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	if (!list_empty(&ctx->hw_block_mem_list))
290962306a36Sopenharmony_ci		dev_crit(ctx->hdev->dev, "HW block mem list isn't empty\n");
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	list_for_each_entry_safe(lnode, tmp, &ctx->hw_block_mem_list, node) {
291262306a36Sopenharmony_ci		list_del(&lnode->node);
291362306a36Sopenharmony_ci		kfree(lnode);
291462306a36Sopenharmony_ci	}
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	mutex_destroy(&ctx->hw_block_list_lock);
291762306a36Sopenharmony_ci}
2918