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