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