18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Copyright 2016-2019 HabanaLabs, Ltd. 58c2ecf20Sopenharmony_ci * All Rights Reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <uapi/misc/habanalabs.h> 98c2ecf20Sopenharmony_ci#include "habanalabs.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 148c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct hl_device *hdev = ctx->hdev; 198c2ecf20Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 208c2ecf20Sopenharmony_ci struct hl_vm_va_block *va_block, *tmp; 218c2ecf20Sopenharmony_ci dma_addr_t bus_addr; 228c2ecf20Sopenharmony_ci u64 virt_addr; 238c2ecf20Sopenharmony_ci u32 page_size = prop->pmmu.page_size; 248c2ecf20Sopenharmony_ci s32 offset; 258c2ecf20Sopenharmony_ci int rc; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (!hdev->supports_cb_mapping) { 288c2ecf20Sopenharmony_ci dev_err_ratelimited(hdev->dev, 298c2ecf20Sopenharmony_ci "Cannot map CB because no VA range is allocated for CB mapping\n"); 308c2ecf20Sopenharmony_ci return -EINVAL; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!hdev->mmu_enable) { 348c2ecf20Sopenharmony_ci dev_err_ratelimited(hdev->dev, 358c2ecf20Sopenharmony_ci "Cannot map CB because MMU is disabled\n"); 368c2ecf20Sopenharmony_ci return -EINVAL; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cb->va_block_list); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (bus_addr = cb->bus_address; 428c2ecf20Sopenharmony_ci bus_addr < cb->bus_address + cb->size; 438c2ecf20Sopenharmony_ci bus_addr += page_size) { 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci virt_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, page_size); 468c2ecf20Sopenharmony_ci if (!virt_addr) { 478c2ecf20Sopenharmony_ci dev_err(hdev->dev, 488c2ecf20Sopenharmony_ci "Failed to allocate device virtual address for CB\n"); 498c2ecf20Sopenharmony_ci rc = -ENOMEM; 508c2ecf20Sopenharmony_ci goto err_va_pool_free; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci va_block = kzalloc(sizeof(*va_block), GFP_KERNEL); 548c2ecf20Sopenharmony_ci if (!va_block) { 558c2ecf20Sopenharmony_ci rc = -ENOMEM; 568c2ecf20Sopenharmony_ci gen_pool_free(ctx->cb_va_pool, virt_addr, page_size); 578c2ecf20Sopenharmony_ci goto err_va_pool_free; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci va_block->start = virt_addr; 618c2ecf20Sopenharmony_ci va_block->end = virt_addr + page_size; 628c2ecf20Sopenharmony_ci va_block->size = page_size; 638c2ecf20Sopenharmony_ci list_add_tail(&va_block->node, &cb->va_block_list); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci mutex_lock(&ctx->mmu_lock); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci bus_addr = cb->bus_address; 698c2ecf20Sopenharmony_ci offset = 0; 708c2ecf20Sopenharmony_ci list_for_each_entry(va_block, &cb->va_block_list, node) { 718c2ecf20Sopenharmony_ci rc = hl_mmu_map(ctx, va_block->start, bus_addr, va_block->size, 728c2ecf20Sopenharmony_ci list_is_last(&va_block->node, 738c2ecf20Sopenharmony_ci &cb->va_block_list)); 748c2ecf20Sopenharmony_ci if (rc) { 758c2ecf20Sopenharmony_ci dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", 768c2ecf20Sopenharmony_ci va_block->start); 778c2ecf20Sopenharmony_ci goto err_va_umap; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci bus_addr += va_block->size; 818c2ecf20Sopenharmony_ci offset += va_block->size; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci hdev->asic_funcs->mmu_invalidate_cache(hdev, false, VM_TYPE_USERPTR); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci mutex_unlock(&ctx->mmu_lock); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci cb->is_mmu_mapped = true; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cierr_va_umap: 938c2ecf20Sopenharmony_ci list_for_each_entry(va_block, &cb->va_block_list, node) { 948c2ecf20Sopenharmony_ci if (offset <= 0) 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci hl_mmu_unmap(ctx, va_block->start, va_block->size, 978c2ecf20Sopenharmony_ci offset <= va_block->size); 988c2ecf20Sopenharmony_ci offset -= va_block->size; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mutex_unlock(&ctx->mmu_lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cierr_va_pool_free: 1068c2ecf20Sopenharmony_ci list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) { 1078c2ecf20Sopenharmony_ci gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size); 1088c2ecf20Sopenharmony_ci list_del(&va_block->node); 1098c2ecf20Sopenharmony_ci kfree(va_block); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return rc; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct hl_device *hdev = ctx->hdev; 1188c2ecf20Sopenharmony_ci struct hl_vm_va_block *va_block, *tmp; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci mutex_lock(&ctx->mmu_lock); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci list_for_each_entry(va_block, &cb->va_block_list, node) 1238c2ecf20Sopenharmony_ci if (hl_mmu_unmap(ctx, va_block->start, va_block->size, 1248c2ecf20Sopenharmony_ci list_is_last(&va_block->node, 1258c2ecf20Sopenharmony_ci &cb->va_block_list))) 1268c2ecf20Sopenharmony_ci dev_warn_ratelimited(hdev->dev, 1278c2ecf20Sopenharmony_ci "Failed to unmap CB's va 0x%llx\n", 1288c2ecf20Sopenharmony_ci va_block->start); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci mutex_unlock(&ctx->mmu_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) { 1358c2ecf20Sopenharmony_ci gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size); 1368c2ecf20Sopenharmony_ci list_del(&va_block->node); 1378c2ecf20Sopenharmony_ci kfree(va_block); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void cb_fini(struct hl_device *hdev, struct hl_cb *cb) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (cb->is_internal) 1448c2ecf20Sopenharmony_ci gen_pool_free(hdev->internal_cb_pool, 1458c2ecf20Sopenharmony_ci (uintptr_t)cb->kernel_address, cb->size); 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, 1488c2ecf20Sopenharmony_ci cb->kernel_address, cb->bus_address); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci kfree(cb); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void cb_do_release(struct hl_device *hdev, struct hl_cb *cb) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci if (cb->is_pool) { 1568c2ecf20Sopenharmony_ci spin_lock(&hdev->cb_pool_lock); 1578c2ecf20Sopenharmony_ci list_add(&cb->pool_list, &hdev->cb_pool); 1588c2ecf20Sopenharmony_ci spin_unlock(&hdev->cb_pool_lock); 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci cb_fini(hdev, cb); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void cb_release(struct kref *ref) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct hl_device *hdev; 1678c2ecf20Sopenharmony_ci struct hl_cb *cb; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci cb = container_of(ref, struct hl_cb, refcount); 1708c2ecf20Sopenharmony_ci hdev = cb->hdev; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci hl_debugfs_remove_cb(cb); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (cb->is_mmu_mapped) 1758c2ecf20Sopenharmony_ci cb_unmap_mem(cb->ctx, cb); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci hl_ctx_put(cb->ctx); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci cb_do_release(hdev, cb); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, 1838c2ecf20Sopenharmony_ci int ctx_id, bool internal_cb) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct hl_cb *cb; 1868c2ecf20Sopenharmony_ci u32 cb_offset; 1878c2ecf20Sopenharmony_ci void *p; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * We use of GFP_ATOMIC here because this function can be called from 1918c2ecf20Sopenharmony_ci * the latency-sensitive code path for command submission. Due to H/W 1928c2ecf20Sopenharmony_ci * limitations in some of the ASICs, the kernel must copy the user CB 1938c2ecf20Sopenharmony_ci * that is designated for an external queue and actually enqueue 1948c2ecf20Sopenharmony_ci * the kernel's copy. Hence, we must never sleep in this code section 1958c2ecf20Sopenharmony_ci * and must use GFP_ATOMIC for all memory allocations. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci if (ctx_id == HL_KERNEL_ASID_ID) 1988c2ecf20Sopenharmony_ci cb = kzalloc(sizeof(*cb), GFP_ATOMIC); 1998c2ecf20Sopenharmony_ci else 2008c2ecf20Sopenharmony_ci cb = kzalloc(sizeof(*cb), GFP_KERNEL); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!cb) 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (internal_cb) { 2068c2ecf20Sopenharmony_ci p = (void *) gen_pool_alloc(hdev->internal_cb_pool, cb_size); 2078c2ecf20Sopenharmony_ci if (!p) { 2088c2ecf20Sopenharmony_ci kfree(cb); 2098c2ecf20Sopenharmony_ci return NULL; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci cb_offset = p - hdev->internal_cb_pool_virt_addr; 2138c2ecf20Sopenharmony_ci cb->is_internal = true; 2148c2ecf20Sopenharmony_ci cb->bus_address = hdev->internal_cb_va_base + cb_offset; 2158c2ecf20Sopenharmony_ci } else if (ctx_id == HL_KERNEL_ASID_ID) { 2168c2ecf20Sopenharmony_ci p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, 2178c2ecf20Sopenharmony_ci &cb->bus_address, GFP_ATOMIC); 2188c2ecf20Sopenharmony_ci } else { 2198c2ecf20Sopenharmony_ci p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, 2208c2ecf20Sopenharmony_ci &cb->bus_address, 2218c2ecf20Sopenharmony_ci GFP_USER | __GFP_ZERO); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (!p) { 2258c2ecf20Sopenharmony_ci dev_err(hdev->dev, 2268c2ecf20Sopenharmony_ci "failed to allocate %d of dma memory for CB\n", 2278c2ecf20Sopenharmony_ci cb_size); 2288c2ecf20Sopenharmony_ci kfree(cb); 2298c2ecf20Sopenharmony_ci return NULL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci cb->kernel_address = p; 2338c2ecf20Sopenharmony_ci cb->size = cb_size; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return cb; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciint hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, 2398c2ecf20Sopenharmony_ci struct hl_ctx *ctx, u32 cb_size, bool internal_cb, 2408c2ecf20Sopenharmony_ci bool map_cb, u64 *handle) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct hl_cb *cb; 2438c2ecf20Sopenharmony_ci bool alloc_new_cb = true; 2448c2ecf20Sopenharmony_ci int rc, ctx_id = ctx->asid; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * Can't use generic function to check this because of special case 2488c2ecf20Sopenharmony_ci * where we create a CB as part of the reset process 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci if ((hdev->disabled) || ((atomic_read(&hdev->in_reset)) && 2518c2ecf20Sopenharmony_ci (ctx_id != HL_KERNEL_ASID_ID))) { 2528c2ecf20Sopenharmony_ci dev_warn_ratelimited(hdev->dev, 2538c2ecf20Sopenharmony_ci "Device is disabled or in reset. Can't create new CBs\n"); 2548c2ecf20Sopenharmony_ci rc = -EBUSY; 2558c2ecf20Sopenharmony_ci goto out_err; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (cb_size > SZ_2M) { 2598c2ecf20Sopenharmony_ci dev_err(hdev->dev, "CB size %d must be less than %d\n", 2608c2ecf20Sopenharmony_ci cb_size, SZ_2M); 2618c2ecf20Sopenharmony_ci rc = -EINVAL; 2628c2ecf20Sopenharmony_ci goto out_err; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!internal_cb) { 2668c2ecf20Sopenharmony_ci /* Minimum allocation must be PAGE SIZE */ 2678c2ecf20Sopenharmony_ci if (cb_size < PAGE_SIZE) 2688c2ecf20Sopenharmony_ci cb_size = PAGE_SIZE; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (ctx_id == HL_KERNEL_ASID_ID && 2718c2ecf20Sopenharmony_ci cb_size <= hdev->asic_prop.cb_pool_cb_size) { 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci spin_lock(&hdev->cb_pool_lock); 2748c2ecf20Sopenharmony_ci if (!list_empty(&hdev->cb_pool)) { 2758c2ecf20Sopenharmony_ci cb = list_first_entry(&hdev->cb_pool, 2768c2ecf20Sopenharmony_ci typeof(*cb), pool_list); 2778c2ecf20Sopenharmony_ci list_del(&cb->pool_list); 2788c2ecf20Sopenharmony_ci spin_unlock(&hdev->cb_pool_lock); 2798c2ecf20Sopenharmony_ci alloc_new_cb = false; 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci spin_unlock(&hdev->cb_pool_lock); 2828c2ecf20Sopenharmony_ci dev_dbg(hdev->dev, "CB pool is empty\n"); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (alloc_new_cb) { 2888c2ecf20Sopenharmony_ci cb = hl_cb_alloc(hdev, cb_size, ctx_id, internal_cb); 2898c2ecf20Sopenharmony_ci if (!cb) { 2908c2ecf20Sopenharmony_ci rc = -ENOMEM; 2918c2ecf20Sopenharmony_ci goto out_err; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci cb->hdev = hdev; 2968c2ecf20Sopenharmony_ci cb->ctx = ctx; 2978c2ecf20Sopenharmony_ci hl_ctx_get(hdev, cb->ctx); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (map_cb) { 3008c2ecf20Sopenharmony_ci if (ctx_id == HL_KERNEL_ASID_ID) { 3018c2ecf20Sopenharmony_ci dev_err(hdev->dev, 3028c2ecf20Sopenharmony_ci "CB mapping is not supported for kernel context\n"); 3038c2ecf20Sopenharmony_ci rc = -EINVAL; 3048c2ecf20Sopenharmony_ci goto release_cb; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci rc = cb_map_mem(ctx, cb); 3088c2ecf20Sopenharmony_ci if (rc) 3098c2ecf20Sopenharmony_ci goto release_cb; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spin_lock(&mgr->cb_lock); 3138c2ecf20Sopenharmony_ci rc = idr_alloc(&mgr->cb_handles, cb, 1, 0, GFP_ATOMIC); 3148c2ecf20Sopenharmony_ci spin_unlock(&mgr->cb_lock); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (rc < 0) { 3178c2ecf20Sopenharmony_ci dev_err(hdev->dev, "Failed to allocate IDR for a new CB\n"); 3188c2ecf20Sopenharmony_ci goto unmap_mem; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci cb->id = (u64) rc; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci kref_init(&cb->refcount); 3248c2ecf20Sopenharmony_ci spin_lock_init(&cb->lock); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * idr is 32-bit so we can safely OR it with a mask that is above 3288c2ecf20Sopenharmony_ci * 32 bit 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci *handle = cb->id | HL_MMAP_TYPE_CB; 3318c2ecf20Sopenharmony_ci *handle <<= PAGE_SHIFT; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci hl_debugfs_add_cb(cb); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciunmap_mem: 3388c2ecf20Sopenharmony_ci if (cb->is_mmu_mapped) 3398c2ecf20Sopenharmony_ci cb_unmap_mem(cb->ctx, cb); 3408c2ecf20Sopenharmony_cirelease_cb: 3418c2ecf20Sopenharmony_ci hl_ctx_put(cb->ctx); 3428c2ecf20Sopenharmony_ci cb_do_release(hdev, cb); 3438c2ecf20Sopenharmony_ciout_err: 3448c2ecf20Sopenharmony_ci *handle = 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return rc; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ciint hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct hl_cb *cb; 3528c2ecf20Sopenharmony_ci u32 handle; 3538c2ecf20Sopenharmony_ci int rc = 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * handle was given to user to do mmap, I need to shift it back to 3578c2ecf20Sopenharmony_ci * how the idr module gave it to me 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci cb_handle >>= PAGE_SHIFT; 3608c2ecf20Sopenharmony_ci handle = (u32) cb_handle; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci spin_lock(&mgr->cb_lock); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci cb = idr_find(&mgr->cb_handles, handle); 3658c2ecf20Sopenharmony_ci if (cb) { 3668c2ecf20Sopenharmony_ci idr_remove(&mgr->cb_handles, handle); 3678c2ecf20Sopenharmony_ci spin_unlock(&mgr->cb_lock); 3688c2ecf20Sopenharmony_ci kref_put(&cb->refcount, cb_release); 3698c2ecf20Sopenharmony_ci } else { 3708c2ecf20Sopenharmony_ci spin_unlock(&mgr->cb_lock); 3718c2ecf20Sopenharmony_ci dev_err(hdev->dev, 3728c2ecf20Sopenharmony_ci "CB destroy failed, no match to handle 0x%x\n", handle); 3738c2ecf20Sopenharmony_ci rc = -EINVAL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return rc; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci union hl_cb_args *args = data; 3828c2ecf20Sopenharmony_ci struct hl_device *hdev = hpriv->hdev; 3838c2ecf20Sopenharmony_ci u64 handle = 0; 3848c2ecf20Sopenharmony_ci int rc; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (hl_device_disabled_or_in_reset(hdev)) { 3878c2ecf20Sopenharmony_ci dev_warn_ratelimited(hdev->dev, 3888c2ecf20Sopenharmony_ci "Device is %s. Can't execute CB IOCTL\n", 3898c2ecf20Sopenharmony_ci atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); 3908c2ecf20Sopenharmony_ci return -EBUSY; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci switch (args->in.op) { 3948c2ecf20Sopenharmony_ci case HL_CB_OP_CREATE: 3958c2ecf20Sopenharmony_ci if (args->in.cb_size > HL_MAX_CB_SIZE) { 3968c2ecf20Sopenharmony_ci dev_err(hdev->dev, 3978c2ecf20Sopenharmony_ci "User requested CB size %d must be less than %d\n", 3988c2ecf20Sopenharmony_ci args->in.cb_size, HL_MAX_CB_SIZE); 3998c2ecf20Sopenharmony_ci rc = -EINVAL; 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci rc = hl_cb_create(hdev, &hpriv->cb_mgr, hpriv->ctx, 4028c2ecf20Sopenharmony_ci args->in.cb_size, false, 4038c2ecf20Sopenharmony_ci !!(args->in.flags & HL_CB_FLAGS_MAP), 4048c2ecf20Sopenharmony_ci &handle); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci memset(args, 0, sizeof(*args)); 4088c2ecf20Sopenharmony_ci args->out.cb_handle = handle; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci case HL_CB_OP_DESTROY: 4128c2ecf20Sopenharmony_ci rc = hl_cb_destroy(hdev, &hpriv->cb_mgr, 4138c2ecf20Sopenharmony_ci args->in.cb_handle); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci default: 4178c2ecf20Sopenharmony_ci rc = -ENOTTY; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return rc; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void cb_vm_close(struct vm_area_struct *vma) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct hl_cb *cb = (struct hl_cb *) vma->vm_private_data; 4278c2ecf20Sopenharmony_ci long new_mmap_size; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci new_mmap_size = cb->mmap_size - (vma->vm_end - vma->vm_start); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (new_mmap_size > 0) { 4328c2ecf20Sopenharmony_ci cb->mmap_size = new_mmap_size; 4338c2ecf20Sopenharmony_ci return; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci spin_lock(&cb->lock); 4378c2ecf20Sopenharmony_ci cb->mmap = false; 4388c2ecf20Sopenharmony_ci spin_unlock(&cb->lock); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci hl_cb_put(cb); 4418c2ecf20Sopenharmony_ci vma->vm_private_data = NULL; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct vm_operations_struct cb_vm_ops = { 4458c2ecf20Sopenharmony_ci .close = cb_vm_close 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciint hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct hl_device *hdev = hpriv->hdev; 4518c2ecf20Sopenharmony_ci struct hl_cb *cb; 4528c2ecf20Sopenharmony_ci u32 handle, user_cb_size; 4538c2ecf20Sopenharmony_ci int rc; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* We use the page offset to hold the idr and thus we need to clear 4568c2ecf20Sopenharmony_ci * it before doing the mmap itself 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci handle = vma->vm_pgoff; 4598c2ecf20Sopenharmony_ci vma->vm_pgoff = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* reference was taken here */ 4628c2ecf20Sopenharmony_ci cb = hl_cb_get(hdev, &hpriv->cb_mgr, handle); 4638c2ecf20Sopenharmony_ci if (!cb) { 4648c2ecf20Sopenharmony_ci dev_err(hdev->dev, 4658c2ecf20Sopenharmony_ci "CB mmap failed, no match to handle 0x%x\n", handle); 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Validation check */ 4708c2ecf20Sopenharmony_ci user_cb_size = vma->vm_end - vma->vm_start; 4718c2ecf20Sopenharmony_ci if (user_cb_size != ALIGN(cb->size, PAGE_SIZE)) { 4728c2ecf20Sopenharmony_ci dev_err(hdev->dev, 4738c2ecf20Sopenharmony_ci "CB mmap failed, mmap size 0x%lx != 0x%x cb size\n", 4748c2ecf20Sopenharmony_ci vma->vm_end - vma->vm_start, cb->size); 4758c2ecf20Sopenharmony_ci rc = -EINVAL; 4768c2ecf20Sopenharmony_ci goto put_cb; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!access_ok((void __user *) (uintptr_t) vma->vm_start, 4808c2ecf20Sopenharmony_ci user_cb_size)) { 4818c2ecf20Sopenharmony_ci dev_err(hdev->dev, 4828c2ecf20Sopenharmony_ci "user pointer is invalid - 0x%lx\n", 4838c2ecf20Sopenharmony_ci vma->vm_start); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rc = -EINVAL; 4868c2ecf20Sopenharmony_ci goto put_cb; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci spin_lock(&cb->lock); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (cb->mmap) { 4928c2ecf20Sopenharmony_ci dev_err(hdev->dev, 4938c2ecf20Sopenharmony_ci "CB mmap failed, CB already mmaped to user\n"); 4948c2ecf20Sopenharmony_ci rc = -EINVAL; 4958c2ecf20Sopenharmony_ci goto release_lock; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci cb->mmap = true; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci spin_unlock(&cb->lock); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci vma->vm_ops = &cb_vm_ops; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Note: We're transferring the cb reference to 5068c2ecf20Sopenharmony_ci * vma->vm_private_data here. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci vma->vm_private_data = cb; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci rc = hdev->asic_funcs->cb_mmap(hdev, vma, cb->kernel_address, 5128c2ecf20Sopenharmony_ci cb->bus_address, cb->size); 5138c2ecf20Sopenharmony_ci if (rc) { 5148c2ecf20Sopenharmony_ci spin_lock(&cb->lock); 5158c2ecf20Sopenharmony_ci cb->mmap = false; 5168c2ecf20Sopenharmony_ci goto release_lock; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci cb->mmap_size = cb->size; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cirelease_lock: 5248c2ecf20Sopenharmony_ci spin_unlock(&cb->lock); 5258c2ecf20Sopenharmony_ciput_cb: 5268c2ecf20Sopenharmony_ci hl_cb_put(cb); 5278c2ecf20Sopenharmony_ci return rc; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistruct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, 5318c2ecf20Sopenharmony_ci u32 handle) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct hl_cb *cb; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock(&mgr->cb_lock); 5368c2ecf20Sopenharmony_ci cb = idr_find(&mgr->cb_handles, handle); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (!cb) { 5398c2ecf20Sopenharmony_ci spin_unlock(&mgr->cb_lock); 5408c2ecf20Sopenharmony_ci dev_warn(hdev->dev, 5418c2ecf20Sopenharmony_ci "CB get failed, no match to handle 0x%x\n", handle); 5428c2ecf20Sopenharmony_ci return NULL; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci kref_get(&cb->refcount); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci spin_unlock(&mgr->cb_lock); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return cb; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_civoid hl_cb_put(struct hl_cb *cb) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci kref_put(&cb->refcount, cb_release); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_civoid hl_cb_mgr_init(struct hl_cb_mgr *mgr) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci spin_lock_init(&mgr->cb_lock); 5618c2ecf20Sopenharmony_ci idr_init(&mgr->cb_handles); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_civoid hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct hl_cb *cb; 5678c2ecf20Sopenharmony_ci struct idr *idp; 5688c2ecf20Sopenharmony_ci u32 id; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci idp = &mgr->cb_handles; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci idr_for_each_entry(idp, cb, id) { 5738c2ecf20Sopenharmony_ci if (kref_put(&cb->refcount, cb_release) != 1) 5748c2ecf20Sopenharmony_ci dev_err(hdev->dev, 5758c2ecf20Sopenharmony_ci "CB %d for CTX ID %d is still alive\n", 5768c2ecf20Sopenharmony_ci id, cb->ctx->asid); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci idr_destroy(&mgr->cb_handles); 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistruct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size, 5838c2ecf20Sopenharmony_ci bool internal_cb) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci u64 cb_handle; 5868c2ecf20Sopenharmony_ci struct hl_cb *cb; 5878c2ecf20Sopenharmony_ci int rc; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, hdev->kernel_ctx, cb_size, 5908c2ecf20Sopenharmony_ci internal_cb, false, &cb_handle); 5918c2ecf20Sopenharmony_ci if (rc) { 5928c2ecf20Sopenharmony_ci dev_err(hdev->dev, 5938c2ecf20Sopenharmony_ci "Failed to allocate CB for the kernel driver %d\n", rc); 5948c2ecf20Sopenharmony_ci return NULL; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci cb_handle >>= PAGE_SHIFT; 5988c2ecf20Sopenharmony_ci cb = hl_cb_get(hdev, &hdev->kernel_cb_mgr, (u32) cb_handle); 5998c2ecf20Sopenharmony_ci /* hl_cb_get should never fail here so use kernel WARN */ 6008c2ecf20Sopenharmony_ci WARN(!cb, "Kernel CB handle invalid 0x%x\n", (u32) cb_handle); 6018c2ecf20Sopenharmony_ci if (!cb) 6028c2ecf20Sopenharmony_ci goto destroy_cb; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return cb; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cidestroy_cb: 6078c2ecf20Sopenharmony_ci hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb_handle << PAGE_SHIFT); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return NULL; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciint hl_cb_pool_init(struct hl_device *hdev) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct hl_cb *cb; 6158c2ecf20Sopenharmony_ci int i; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hdev->cb_pool); 6188c2ecf20Sopenharmony_ci spin_lock_init(&hdev->cb_pool_lock); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) { 6218c2ecf20Sopenharmony_ci cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size, 6228c2ecf20Sopenharmony_ci HL_KERNEL_ASID_ID, false); 6238c2ecf20Sopenharmony_ci if (cb) { 6248c2ecf20Sopenharmony_ci cb->is_pool = true; 6258c2ecf20Sopenharmony_ci list_add(&cb->pool_list, &hdev->cb_pool); 6268c2ecf20Sopenharmony_ci } else { 6278c2ecf20Sopenharmony_ci hl_cb_pool_fini(hdev); 6288c2ecf20Sopenharmony_ci return -ENOMEM; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ciint hl_cb_pool_fini(struct hl_device *hdev) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct hl_cb *cb, *tmp; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, tmp, &hdev->cb_pool, pool_list) { 6408c2ecf20Sopenharmony_ci list_del(&cb->pool_list); 6418c2ecf20Sopenharmony_ci cb_fini(hdev, cb); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ciint hl_cb_va_pool_init(struct hl_ctx *ctx) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct hl_device *hdev = ctx->hdev; 6508c2ecf20Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 6518c2ecf20Sopenharmony_ci int rc; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (!hdev->supports_cb_mapping) 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci ctx->cb_va_pool = gen_pool_create(__ffs(prop->pmmu.page_size), -1); 6578c2ecf20Sopenharmony_ci if (!ctx->cb_va_pool) { 6588c2ecf20Sopenharmony_ci dev_err(hdev->dev, 6598c2ecf20Sopenharmony_ci "Failed to create VA gen pool for CB mapping\n"); 6608c2ecf20Sopenharmony_ci return -ENOMEM; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci rc = gen_pool_add(ctx->cb_va_pool, prop->cb_va_start_addr, 6648c2ecf20Sopenharmony_ci prop->cb_va_end_addr - prop->cb_va_start_addr, -1); 6658c2ecf20Sopenharmony_ci if (rc) { 6668c2ecf20Sopenharmony_ci dev_err(hdev->dev, 6678c2ecf20Sopenharmony_ci "Failed to add memory to VA gen pool for CB mapping\n"); 6688c2ecf20Sopenharmony_ci goto err_pool_destroy; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cierr_pool_destroy: 6748c2ecf20Sopenharmony_ci gen_pool_destroy(ctx->cb_va_pool); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return rc; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_civoid hl_cb_va_pool_fini(struct hl_ctx *ctx) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct hl_device *hdev = ctx->hdev; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (!hdev->supports_cb_mapping) 6848c2ecf20Sopenharmony_ci return; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci gen_pool_destroy(ctx->cb_va_pool); 6878c2ecf20Sopenharmony_ci} 688