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 "habanalabs.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic void hl_ctx_fini(struct hl_ctx *ctx) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct hl_device *hdev = ctx->hdev; 158c2ecf20Sopenharmony_ci u64 idle_mask = 0; 168c2ecf20Sopenharmony_ci int i; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci /* 198c2ecf20Sopenharmony_ci * If we arrived here, there are no jobs waiting for this context 208c2ecf20Sopenharmony_ci * on its queues so we can safely remove it. 218c2ecf20Sopenharmony_ci * This is because for each CS, we increment the ref count and for 228c2ecf20Sopenharmony_ci * every CS that was finished we decrement it and we won't arrive 238c2ecf20Sopenharmony_ci * to this function unless the ref count is 0 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++) 278c2ecf20Sopenharmony_ci hl_fence_put(ctx->cs_pending[i]); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci kfree(ctx->cs_pending); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (ctx->asid != HL_KERNEL_ASID_ID) { 328c2ecf20Sopenharmony_ci dev_dbg(hdev->dev, "closing user context %d\n", ctx->asid); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* The engines are stopped as there is no executing CS, but the 358c2ecf20Sopenharmony_ci * Coresight might be still working by accessing addresses 368c2ecf20Sopenharmony_ci * related to the stopped engines. Hence stop it explicitly. 378c2ecf20Sopenharmony_ci * Stop only if this is the compute context, as there can be 388c2ecf20Sopenharmony_ci * only one compute context 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci if ((hdev->in_debug) && (hdev->compute_ctx == ctx)) 418c2ecf20Sopenharmony_ci hl_device_set_debug_mode(hdev, false); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci hl_cb_va_pool_fini(ctx); 448c2ecf20Sopenharmony_ci hl_vm_ctx_fini(ctx); 458c2ecf20Sopenharmony_ci hl_asid_free(hdev, ctx->asid); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if ((!hdev->pldm) && (hdev->pdev) && 488c2ecf20Sopenharmony_ci (!hdev->asic_funcs->is_device_idle(hdev, 498c2ecf20Sopenharmony_ci &idle_mask, NULL))) 508c2ecf20Sopenharmony_ci dev_notice(hdev->dev, 518c2ecf20Sopenharmony_ci "device not idle after user context is closed (0x%llx)\n", 528c2ecf20Sopenharmony_ci idle_mask); 538c2ecf20Sopenharmony_ci } else { 548c2ecf20Sopenharmony_ci dev_dbg(hdev->dev, "closing kernel context\n"); 558c2ecf20Sopenharmony_ci hl_mmu_ctx_fini(ctx); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid hl_ctx_do_release(struct kref *ref) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct hl_ctx *ctx; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ctx = container_of(ref, struct hl_ctx, refcount); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci hl_ctx_fini(ctx); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (ctx->hpriv) 688c2ecf20Sopenharmony_ci hl_hpriv_put(ctx->hpriv); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci kfree(ctx); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct hl_ctx_mgr *mgr = &hpriv->ctx_mgr; 768c2ecf20Sopenharmony_ci struct hl_ctx *ctx; 778c2ecf20Sopenharmony_ci int rc; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 808c2ecf20Sopenharmony_ci if (!ctx) { 818c2ecf20Sopenharmony_ci rc = -ENOMEM; 828c2ecf20Sopenharmony_ci goto out_err; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_lock(&mgr->ctx_lock); 868c2ecf20Sopenharmony_ci rc = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL); 878c2ecf20Sopenharmony_ci mutex_unlock(&mgr->ctx_lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (rc < 0) { 908c2ecf20Sopenharmony_ci dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n"); 918c2ecf20Sopenharmony_ci goto free_ctx; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ctx->handle = rc; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci rc = hl_ctx_init(hdev, ctx, false); 978c2ecf20Sopenharmony_ci if (rc) 988c2ecf20Sopenharmony_ci goto remove_from_idr; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci hl_hpriv_get(hpriv); 1018c2ecf20Sopenharmony_ci ctx->hpriv = hpriv; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* TODO: remove for multiple contexts per process */ 1048c2ecf20Sopenharmony_ci hpriv->ctx = ctx; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* TODO: remove the following line for multiple process support */ 1078c2ecf20Sopenharmony_ci hdev->compute_ctx = ctx; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciremove_from_idr: 1128c2ecf20Sopenharmony_ci mutex_lock(&mgr->ctx_lock); 1138c2ecf20Sopenharmony_ci idr_remove(&mgr->ctx_handles, ctx->handle); 1148c2ecf20Sopenharmony_ci mutex_unlock(&mgr->ctx_lock); 1158c2ecf20Sopenharmony_cifree_ctx: 1168c2ecf20Sopenharmony_ci kfree(ctx); 1178c2ecf20Sopenharmony_ciout_err: 1188c2ecf20Sopenharmony_ci return rc; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1) 1248c2ecf20Sopenharmony_ci return; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci dev_warn(hdev->dev, 1278c2ecf20Sopenharmony_ci "user process released device but its command submissions are still executing\n"); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciint hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int rc = 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ctx->hdev = hdev; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci kref_init(&ctx->refcount); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ctx->cs_sequence = 1; 1398c2ecf20Sopenharmony_ci spin_lock_init(&ctx->cs_lock); 1408c2ecf20Sopenharmony_ci atomic_set(&ctx->thread_ctx_switch_token, 1); 1418c2ecf20Sopenharmony_ci ctx->thread_ctx_switch_wait_token = 0; 1428c2ecf20Sopenharmony_ci ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs, 1438c2ecf20Sopenharmony_ci sizeof(struct hl_fence *), 1448c2ecf20Sopenharmony_ci GFP_KERNEL); 1458c2ecf20Sopenharmony_ci if (!ctx->cs_pending) 1468c2ecf20Sopenharmony_ci return -ENOMEM; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (is_kernel_ctx) { 1498c2ecf20Sopenharmony_ci ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */ 1508c2ecf20Sopenharmony_ci rc = hl_mmu_ctx_init(ctx); 1518c2ecf20Sopenharmony_ci if (rc) { 1528c2ecf20Sopenharmony_ci dev_err(hdev->dev, "Failed to init mmu ctx module\n"); 1538c2ecf20Sopenharmony_ci goto err_free_cs_pending; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci ctx->asid = hl_asid_alloc(hdev); 1578c2ecf20Sopenharmony_ci if (!ctx->asid) { 1588c2ecf20Sopenharmony_ci dev_err(hdev->dev, "No free ASID, failed to create context\n"); 1598c2ecf20Sopenharmony_ci rc = -ENOMEM; 1608c2ecf20Sopenharmony_ci goto err_free_cs_pending; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci rc = hl_vm_ctx_init(ctx); 1648c2ecf20Sopenharmony_ci if (rc) { 1658c2ecf20Sopenharmony_ci dev_err(hdev->dev, "Failed to init mem ctx module\n"); 1668c2ecf20Sopenharmony_ci rc = -ENOMEM; 1678c2ecf20Sopenharmony_ci goto err_asid_free; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci rc = hl_cb_va_pool_init(ctx); 1718c2ecf20Sopenharmony_ci if (rc) { 1728c2ecf20Sopenharmony_ci dev_err(hdev->dev, 1738c2ecf20Sopenharmony_ci "Failed to init VA pool for mapped CB\n"); 1748c2ecf20Sopenharmony_ci goto err_vm_ctx_fini; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci rc = hdev->asic_funcs->ctx_init(ctx); 1788c2ecf20Sopenharmony_ci if (rc) { 1798c2ecf20Sopenharmony_ci dev_err(hdev->dev, "ctx_init failed\n"); 1808c2ecf20Sopenharmony_ci goto err_cb_va_pool_fini; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci dev_dbg(hdev->dev, "create user context %d\n", ctx->asid); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cierr_cb_va_pool_fini: 1898c2ecf20Sopenharmony_ci hl_cb_va_pool_fini(ctx); 1908c2ecf20Sopenharmony_cierr_vm_ctx_fini: 1918c2ecf20Sopenharmony_ci hl_vm_ctx_fini(ctx); 1928c2ecf20Sopenharmony_cierr_asid_free: 1938c2ecf20Sopenharmony_ci hl_asid_free(hdev, ctx->asid); 1948c2ecf20Sopenharmony_cierr_free_cs_pending: 1958c2ecf20Sopenharmony_ci kfree(ctx->cs_pending); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return rc; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci kref_get(&ctx->refcount); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciint hl_ctx_put(struct hl_ctx *ctx) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci return kref_put(&ctx->refcount, hl_ctx_do_release); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop; 2138c2ecf20Sopenharmony_ci struct hl_fence *fence; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci spin_lock(&ctx->cs_lock); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (seq >= ctx->cs_sequence) { 2188c2ecf20Sopenharmony_ci spin_unlock(&ctx->cs_lock); 2198c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (seq + asic_prop->max_pending_cs < ctx->cs_sequence) { 2238c2ecf20Sopenharmony_ci spin_unlock(&ctx->cs_lock); 2248c2ecf20Sopenharmony_ci return NULL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci fence = ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)]; 2288c2ecf20Sopenharmony_ci hl_fence_get(fence); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci spin_unlock(&ctx->cs_lock); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return fence; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * hl_ctx_mgr_init - initialize the context manager 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * @mgr: pointer to context manager structure 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * This manager is an object inside the hpriv object of the user process. 2418c2ecf20Sopenharmony_ci * The function is called when a user process opens the FD. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_civoid hl_ctx_mgr_init(struct hl_ctx_mgr *mgr) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci mutex_init(&mgr->ctx_lock); 2468c2ecf20Sopenharmony_ci idr_init(&mgr->ctx_handles); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * hl_ctx_mgr_fini - finalize the context manager 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * @hdev: pointer to device structure 2538c2ecf20Sopenharmony_ci * @mgr: pointer to context manager structure 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * This function goes over all the contexts in the manager and frees them. 2568c2ecf20Sopenharmony_ci * It is called when a process closes the FD. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_civoid hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct hl_ctx *ctx; 2618c2ecf20Sopenharmony_ci struct idr *idp; 2628c2ecf20Sopenharmony_ci u32 id; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci idp = &mgr->ctx_handles; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci idr_for_each_entry(idp, ctx, id) 2678c2ecf20Sopenharmony_ci hl_ctx_free(hdev, ctx); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci idr_destroy(&mgr->ctx_handles); 2708c2ecf20Sopenharmony_ci mutex_destroy(&mgr->ctx_lock); 2718c2ecf20Sopenharmony_ci} 272