linux/drivers/misc/habanalabs/common/context.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/*
   4 * Copyright 2016-2019 HabanaLabs, Ltd.
   5 * All Rights Reserved.
   6 */
   7
   8#include "habanalabs.h"
   9
  10#include <linux/slab.h>
  11
  12static void hl_ctx_fini(struct hl_ctx *ctx)
  13{
  14        struct hl_device *hdev = ctx->hdev;
  15        int i;
  16
  17        /*
  18         * If we arrived here, there are no jobs waiting for this context
  19         * on its queues so we can safely remove it.
  20         * This is because for each CS, we increment the ref count and for
  21         * every CS that was finished we decrement it and we won't arrive
  22         * to this function unless the ref count is 0
  23         */
  24
  25        for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++)
  26                dma_fence_put(ctx->cs_pending[i]);
  27
  28        kfree(ctx->cs_pending);
  29
  30        if (ctx->asid != HL_KERNEL_ASID_ID) {
  31                /* The engines are stopped as there is no executing CS, but the
  32                 * Coresight might be still working by accessing addresses
  33                 * related to the stopped engines. Hence stop it explicitly.
  34                 * Stop only if this is the compute context, as there can be
  35                 * only one compute context
  36                 */
  37                if ((hdev->in_debug) && (hdev->compute_ctx == ctx))
  38                        hl_device_set_debug_mode(hdev, false);
  39
  40                hl_vm_ctx_fini(ctx);
  41                hl_asid_free(hdev, ctx->asid);
  42        } else {
  43                hl_mmu_ctx_fini(ctx);
  44        }
  45}
  46
  47void hl_ctx_do_release(struct kref *ref)
  48{
  49        struct hl_ctx *ctx;
  50
  51        ctx = container_of(ref, struct hl_ctx, refcount);
  52
  53        hl_ctx_fini(ctx);
  54
  55        if (ctx->hpriv)
  56                hl_hpriv_put(ctx->hpriv);
  57
  58        kfree(ctx);
  59}
  60
  61int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv)
  62{
  63        struct hl_ctx_mgr *mgr = &hpriv->ctx_mgr;
  64        struct hl_ctx *ctx;
  65        int rc;
  66
  67        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
  68        if (!ctx) {
  69                rc = -ENOMEM;
  70                goto out_err;
  71        }
  72
  73        mutex_lock(&mgr->ctx_lock);
  74        rc = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL);
  75        mutex_unlock(&mgr->ctx_lock);
  76
  77        if (rc < 0) {
  78                dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n");
  79                goto free_ctx;
  80        }
  81
  82        ctx->handle = rc;
  83
  84        rc = hl_ctx_init(hdev, ctx, false);
  85        if (rc)
  86                goto remove_from_idr;
  87
  88        hl_hpriv_get(hpriv);
  89        ctx->hpriv = hpriv;
  90
  91        /* TODO: remove for multiple contexts per process */
  92        hpriv->ctx = ctx;
  93
  94        /* TODO: remove the following line for multiple process support */
  95        hdev->compute_ctx = ctx;
  96
  97        return 0;
  98
  99remove_from_idr:
 100        mutex_lock(&mgr->ctx_lock);
 101        idr_remove(&mgr->ctx_handles, ctx->handle);
 102        mutex_unlock(&mgr->ctx_lock);
 103free_ctx:
 104        kfree(ctx);
 105out_err:
 106        return rc;
 107}
 108
 109void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx)
 110{
 111        if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1)
 112                return;
 113
 114        dev_warn(hdev->dev,
 115                "user process released device but its command submissions are still executing\n");
 116}
 117
 118int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx)
 119{
 120        int rc = 0;
 121
 122        ctx->hdev = hdev;
 123
 124        kref_init(&ctx->refcount);
 125
 126        ctx->cs_sequence = 1;
 127        spin_lock_init(&ctx->cs_lock);
 128        atomic_set(&ctx->thread_ctx_switch_token, 1);
 129        ctx->thread_ctx_switch_wait_token = 0;
 130        ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs,
 131                                sizeof(struct dma_fence *),
 132                                GFP_KERNEL);
 133        if (!ctx->cs_pending)
 134                return -ENOMEM;
 135
 136        if (is_kernel_ctx) {
 137                ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */
 138                rc = hl_mmu_ctx_init(ctx);
 139                if (rc) {
 140                        dev_err(hdev->dev, "Failed to init mmu ctx module\n");
 141                        goto err_free_cs_pending;
 142                }
 143        } else {
 144                ctx->asid = hl_asid_alloc(hdev);
 145                if (!ctx->asid) {
 146                        dev_err(hdev->dev, "No free ASID, failed to create context\n");
 147                        rc = -ENOMEM;
 148                        goto err_free_cs_pending;
 149                }
 150
 151                rc = hl_vm_ctx_init(ctx);
 152                if (rc) {
 153                        dev_err(hdev->dev, "Failed to init mem ctx module\n");
 154                        rc = -ENOMEM;
 155                        goto err_asid_free;
 156                }
 157
 158                rc = hdev->asic_funcs->ctx_init(ctx);
 159                if (rc) {
 160                        dev_err(hdev->dev, "ctx_init failed\n");
 161                        goto err_vm_ctx_fini;
 162                }
 163        }
 164
 165        return 0;
 166
 167err_vm_ctx_fini:
 168        hl_vm_ctx_fini(ctx);
 169err_asid_free:
 170        hl_asid_free(hdev, ctx->asid);
 171err_free_cs_pending:
 172        kfree(ctx->cs_pending);
 173
 174        return rc;
 175}
 176
 177void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx)
 178{
 179        kref_get(&ctx->refcount);
 180}
 181
 182int hl_ctx_put(struct hl_ctx *ctx)
 183{
 184        return kref_put(&ctx->refcount, hl_ctx_do_release);
 185}
 186
 187struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq)
 188{
 189        struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop;
 190        struct dma_fence *fence;
 191
 192        spin_lock(&ctx->cs_lock);
 193
 194        if (seq >= ctx->cs_sequence) {
 195                spin_unlock(&ctx->cs_lock);
 196                return ERR_PTR(-EINVAL);
 197        }
 198
 199        if (seq + asic_prop->max_pending_cs < ctx->cs_sequence) {
 200                spin_unlock(&ctx->cs_lock);
 201                return NULL;
 202        }
 203
 204        fence = dma_fence_get(
 205                        ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)]);
 206        spin_unlock(&ctx->cs_lock);
 207
 208        return fence;
 209}
 210
 211/*
 212 * hl_ctx_mgr_init - initialize the context manager
 213 *
 214 * @mgr: pointer to context manager structure
 215 *
 216 * This manager is an object inside the hpriv object of the user process.
 217 * The function is called when a user process opens the FD.
 218 */
 219void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr)
 220{
 221        mutex_init(&mgr->ctx_lock);
 222        idr_init(&mgr->ctx_handles);
 223}
 224
 225/*
 226 * hl_ctx_mgr_fini - finalize the context manager
 227 *
 228 * @hdev: pointer to device structure
 229 * @mgr: pointer to context manager structure
 230 *
 231 * This function goes over all the contexts in the manager and frees them.
 232 * It is called when a process closes the FD.
 233 */
 234void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr)
 235{
 236        struct hl_ctx *ctx;
 237        struct idr *idp;
 238        u32 id;
 239
 240        idp = &mgr->ctx_handles;
 241
 242        idr_for_each_entry(idp, ctx, id)
 243                hl_ctx_free(hdev, ctx);
 244
 245        idr_destroy(&mgr->ctx_handles);
 246        mutex_destroy(&mgr->ctx_lock);
 247}
 248