Path: blob/master/drivers/accel/habanalabs/common/context.c
26436 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2016-2021 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include "habanalabs.h"89#include <linux/slab.h>1011static void encaps_handle_do_release(struct hl_cs_encaps_sig_handle *handle, bool put_hw_sob,12bool put_ctx)13{14struct hl_encaps_signals_mgr *mgr = &handle->ctx->sig_mgr;1516if (put_hw_sob)17hw_sob_put(handle->hw_sob);1819spin_lock(&mgr->lock);20idr_remove(&mgr->handles, handle->id);21spin_unlock(&mgr->lock);2223if (put_ctx)24hl_ctx_put(handle->ctx);2526kfree(handle);27}2829void hl_encaps_release_handle_and_put_ctx(struct kref *ref)30{31struct hl_cs_encaps_sig_handle *handle =32container_of(ref, struct hl_cs_encaps_sig_handle, refcount);3334encaps_handle_do_release(handle, false, true);35}3637static void hl_encaps_release_handle_and_put_sob(struct kref *ref)38{39struct hl_cs_encaps_sig_handle *handle =40container_of(ref, struct hl_cs_encaps_sig_handle, refcount);4142encaps_handle_do_release(handle, true, false);43}4445void hl_encaps_release_handle_and_put_sob_ctx(struct kref *ref)46{47struct hl_cs_encaps_sig_handle *handle =48container_of(ref, struct hl_cs_encaps_sig_handle, refcount);4950encaps_handle_do_release(handle, true, true);51}5253static void hl_encaps_sig_mgr_init(struct hl_encaps_signals_mgr *mgr)54{55spin_lock_init(&mgr->lock);56idr_init(&mgr->handles);57}5859static void hl_encaps_sig_mgr_fini(struct hl_device *hdev, struct hl_encaps_signals_mgr *mgr)60{61struct hl_cs_encaps_sig_handle *handle;62struct idr *idp;63u32 id;6465idp = &mgr->handles;6667/* The IDR is expected to be empty at this stage, because any left signal should have been68* released as part of CS roll-back.69*/70if (!idr_is_empty(idp)) {71dev_warn(hdev->dev,72"device released while some encaps signals handles are still allocated\n");73idr_for_each_entry(idp, handle, id)74kref_put(&handle->refcount, hl_encaps_release_handle_and_put_sob);75}7677idr_destroy(&mgr->handles);78}7980static void hl_ctx_fini(struct hl_ctx *ctx)81{82struct hl_device *hdev = ctx->hdev;83int i;8485/* Release all allocated HW block mapped list entries and destroy86* the mutex.87*/88hl_hw_block_mem_fini(ctx);8990/*91* If we arrived here, there are no jobs waiting for this context92* on its queues so we can safely remove it.93* This is because for each CS, we increment the ref count and for94* every CS that was finished we decrement it and we won't arrive95* to this function unless the ref count is 096*/9798for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++)99hl_fence_put(ctx->cs_pending[i]);100101kfree(ctx->cs_pending);102103if (ctx->asid != HL_KERNEL_ASID_ID) {104dev_dbg(hdev->dev, "closing user context, asid=%u\n", ctx->asid);105106/* The engines are stopped as there is no executing CS, but the107* Coresight might be still working by accessing addresses108* related to the stopped engines. Hence stop it explicitly.109*/110if (hdev->in_debug)111hl_device_set_debug_mode(hdev, ctx, false);112113hdev->asic_funcs->ctx_fini(ctx);114115hl_dec_ctx_fini(ctx);116117hl_cb_va_pool_fini(ctx);118hl_vm_ctx_fini(ctx);119hl_asid_free(hdev, ctx->asid);120hl_encaps_sig_mgr_fini(hdev, &ctx->sig_mgr);121mutex_destroy(&ctx->ts_reg_lock);122} else {123dev_dbg(hdev->dev, "closing kernel context\n");124hdev->asic_funcs->ctx_fini(ctx);125hl_vm_ctx_fini(ctx);126hl_mmu_ctx_fini(ctx);127}128}129130void hl_ctx_do_release(struct kref *ref)131{132struct hl_ctx *ctx;133134ctx = container_of(ref, struct hl_ctx, refcount);135136hl_ctx_fini(ctx);137138if (ctx->hpriv) {139struct hl_fpriv *hpriv = ctx->hpriv;140141mutex_lock(&hpriv->ctx_lock);142hpriv->ctx = NULL;143mutex_unlock(&hpriv->ctx_lock);144145hl_hpriv_put(hpriv);146}147148kfree(ctx);149}150151int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv)152{153struct hl_ctx_mgr *ctx_mgr = &hpriv->ctx_mgr;154struct hl_ctx *ctx;155int rc;156157ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);158if (!ctx) {159rc = -ENOMEM;160goto out_err;161}162163mutex_lock(&ctx_mgr->lock);164rc = idr_alloc(&ctx_mgr->handles, ctx, 1, 0, GFP_KERNEL);165mutex_unlock(&ctx_mgr->lock);166167if (rc < 0) {168dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n");169goto free_ctx;170}171172ctx->handle = rc;173174rc = hl_ctx_init(hdev, ctx, false);175if (rc)176goto remove_from_idr;177178hl_hpriv_get(hpriv);179ctx->hpriv = hpriv;180181/* TODO: remove for multiple contexts per process */182hpriv->ctx = ctx;183184/* TODO: remove the following line for multiple process support */185hdev->is_compute_ctx_active = true;186187return 0;188189remove_from_idr:190mutex_lock(&ctx_mgr->lock);191idr_remove(&ctx_mgr->handles, ctx->handle);192mutex_unlock(&ctx_mgr->lock);193free_ctx:194kfree(ctx);195out_err:196return rc;197}198199int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx)200{201int rc = 0, i;202203ctx->hdev = hdev;204205kref_init(&ctx->refcount);206207ctx->cs_sequence = 1;208spin_lock_init(&ctx->cs_lock);209atomic_set(&ctx->thread_ctx_switch_token, 1);210ctx->thread_ctx_switch_wait_token = 0;211ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs,212sizeof(struct hl_fence *),213GFP_KERNEL);214if (!ctx->cs_pending)215return -ENOMEM;216217INIT_LIST_HEAD(&ctx->outcome_store.used_list);218INIT_LIST_HEAD(&ctx->outcome_store.free_list);219hash_init(ctx->outcome_store.outcome_map);220for (i = 0; i < ARRAY_SIZE(ctx->outcome_store.nodes_pool); ++i)221list_add(&ctx->outcome_store.nodes_pool[i].list_link,222&ctx->outcome_store.free_list);223224hl_hw_block_mem_init(ctx);225226if (is_kernel_ctx) {227ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */228rc = hl_vm_ctx_init(ctx);229if (rc) {230dev_err(hdev->dev, "Failed to init mem ctx module\n");231rc = -ENOMEM;232goto err_hw_block_mem_fini;233}234235rc = hdev->asic_funcs->ctx_init(ctx);236if (rc) {237dev_err(hdev->dev, "ctx_init failed\n");238goto err_vm_ctx_fini;239}240} else {241ctx->asid = hl_asid_alloc(hdev);242if (!ctx->asid) {243dev_err(hdev->dev, "No free ASID, failed to create context\n");244rc = -ENOMEM;245goto err_hw_block_mem_fini;246}247248rc = hl_vm_ctx_init(ctx);249if (rc) {250dev_err(hdev->dev, "Failed to init mem ctx module\n");251rc = -ENOMEM;252goto err_asid_free;253}254255rc = hl_cb_va_pool_init(ctx);256if (rc) {257dev_err(hdev->dev,258"Failed to init VA pool for mapped CB\n");259goto err_vm_ctx_fini;260}261262rc = hdev->asic_funcs->ctx_init(ctx);263if (rc) {264dev_err(hdev->dev, "ctx_init failed\n");265goto err_cb_va_pool_fini;266}267268hl_encaps_sig_mgr_init(&ctx->sig_mgr);269270mutex_init(&ctx->ts_reg_lock);271272dev_dbg(hdev->dev, "create user context, comm=\"%s\", asid=%u\n",273current->comm, ctx->asid);274}275276return 0;277278err_cb_va_pool_fini:279hl_cb_va_pool_fini(ctx);280err_vm_ctx_fini:281hl_vm_ctx_fini(ctx);282err_asid_free:283if (ctx->asid != HL_KERNEL_ASID_ID)284hl_asid_free(hdev, ctx->asid);285err_hw_block_mem_fini:286hl_hw_block_mem_fini(ctx);287kfree(ctx->cs_pending);288289return rc;290}291292static int hl_ctx_get_unless_zero(struct hl_ctx *ctx)293{294return kref_get_unless_zero(&ctx->refcount);295}296297void hl_ctx_get(struct hl_ctx *ctx)298{299kref_get(&ctx->refcount);300}301302int hl_ctx_put(struct hl_ctx *ctx)303{304return kref_put(&ctx->refcount, hl_ctx_do_release);305}306307struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev)308{309struct hl_ctx *ctx = NULL;310struct hl_fpriv *hpriv;311312mutex_lock(&hdev->fpriv_list_lock);313314list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) {315mutex_lock(&hpriv->ctx_lock);316ctx = hpriv->ctx;317if (ctx && !hl_ctx_get_unless_zero(ctx))318ctx = NULL;319mutex_unlock(&hpriv->ctx_lock);320321/* There can only be a single user which has opened the compute device, so exit322* immediately once we find its context or if we see that it has been released323*/324break;325}326327mutex_unlock(&hdev->fpriv_list_lock);328329return ctx;330}331332/*333* hl_ctx_get_fence_locked - get CS fence under CS lock334*335* @ctx: pointer to the context structure.336* @seq: CS sequences number337*338* @return valid fence pointer on success, NULL if fence is gone, otherwise339* error pointer.340*341* NOTE: this function shall be called with cs_lock locked342*/343static struct hl_fence *hl_ctx_get_fence_locked(struct hl_ctx *ctx, u64 seq)344{345struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop;346struct hl_fence *fence;347348if (seq >= ctx->cs_sequence)349return ERR_PTR(-EINVAL);350351if (seq + asic_prop->max_pending_cs < ctx->cs_sequence)352return NULL;353354fence = ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)];355hl_fence_get(fence);356return fence;357}358359struct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq)360{361struct hl_fence *fence;362363spin_lock(&ctx->cs_lock);364365fence = hl_ctx_get_fence_locked(ctx, seq);366367spin_unlock(&ctx->cs_lock);368369return fence;370}371372/*373* hl_ctx_get_fences - get multiple CS fences under the same CS lock374*375* @ctx: pointer to the context structure.376* @seq_arr: array of CS sequences to wait for377* @fence: fence array to store the CS fences378* @arr_len: length of seq_arr and fence_arr379*380* @return 0 on success, otherwise non 0 error code381*/382int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr,383struct hl_fence **fence, u32 arr_len)384{385struct hl_fence **fence_arr_base = fence;386int i, rc = 0;387388spin_lock(&ctx->cs_lock);389390for (i = 0; i < arr_len; i++, fence++) {391u64 seq = seq_arr[i];392393*fence = hl_ctx_get_fence_locked(ctx, seq);394395if (IS_ERR(*fence)) {396dev_err(ctx->hdev->dev,397"Failed to get fence for CS with seq 0x%llx\n",398seq);399rc = PTR_ERR(*fence);400break;401}402}403404spin_unlock(&ctx->cs_lock);405406if (rc)407hl_fences_put(fence_arr_base, i);408409return rc;410}411412/*413* hl_ctx_mgr_init - initialize the context manager414*415* @ctx_mgr: pointer to context manager structure416*417* This manager is an object inside the hpriv object of the user process.418* The function is called when a user process opens the FD.419*/420void hl_ctx_mgr_init(struct hl_ctx_mgr *ctx_mgr)421{422mutex_init(&ctx_mgr->lock);423idr_init(&ctx_mgr->handles);424}425426/*427* hl_ctx_mgr_fini - finalize the context manager428*429* @hdev: pointer to device structure430* @ctx_mgr: pointer to context manager structure431*432* This function goes over all the contexts in the manager and frees them.433* It is called when a process closes the FD.434*/435void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *ctx_mgr)436{437struct hl_ctx *ctx;438struct idr *idp;439u32 id;440441idp = &ctx_mgr->handles;442443idr_for_each_entry(idp, ctx, id)444kref_put(&ctx->refcount, hl_ctx_do_release);445446idr_destroy(&ctx_mgr->handles);447mutex_destroy(&ctx_mgr->lock);448}449450451