Merge branch 'for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/dennis/percpu
[linux-2.6-microblaze.git] / drivers / misc / habanalabs / context.c
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
12 static 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 < HL_MAX_PENDING_CS ; i++)
26                 dma_fence_put(ctx->cs_pending[i]);
27
28         if (ctx->asid != HL_KERNEL_ASID_ID) {
29                 /*
30                  * The engines are stopped as there is no executing CS, but the
31                  * Coresight might be still working by accessing addresses
32                  * related to the stopped engines. Hence stop it explicitly.
33                  */
34                 if (hdev->in_debug)
35                         hl_device_set_debug_mode(hdev, false);
36
37                 hl_vm_ctx_fini(ctx);
38                 hl_asid_free(hdev, ctx->asid);
39         } else {
40                 hl_mmu_ctx_fini(ctx);
41         }
42 }
43
44 void hl_ctx_do_release(struct kref *ref)
45 {
46         struct hl_ctx *ctx;
47
48         ctx = container_of(ref, struct hl_ctx, refcount);
49
50         hl_ctx_fini(ctx);
51
52         if (ctx->hpriv)
53                 hl_hpriv_put(ctx->hpriv);
54
55         kfree(ctx);
56 }
57
58 int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv)
59 {
60         struct hl_ctx_mgr *mgr = &hpriv->ctx_mgr;
61         struct hl_ctx *ctx;
62         int rc;
63
64         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
65         if (!ctx) {
66                 rc = -ENOMEM;
67                 goto out_err;
68         }
69
70         rc = hl_ctx_init(hdev, ctx, false);
71         if (rc)
72                 goto free_ctx;
73
74         hl_hpriv_get(hpriv);
75         ctx->hpriv = hpriv;
76
77         /* TODO: remove for multiple contexts */
78         hpriv->ctx = ctx;
79         hdev->user_ctx = ctx;
80
81         mutex_lock(&mgr->ctx_lock);
82         rc = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL);
83         mutex_unlock(&mgr->ctx_lock);
84
85         if (rc < 0) {
86                 dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n");
87                 hl_ctx_free(hdev, ctx);
88                 goto out_err;
89         }
90
91         return 0;
92
93 free_ctx:
94         kfree(ctx);
95 out_err:
96         return rc;
97 }
98
99 void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx)
100 {
101         if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1)
102                 return;
103
104         dev_warn(hdev->dev,
105                 "Context %d closed or terminated but its CS are executing\n",
106                 ctx->asid);
107 }
108
109 int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx)
110 {
111         int rc = 0;
112
113         ctx->hdev = hdev;
114
115         kref_init(&ctx->refcount);
116
117         ctx->cs_sequence = 1;
118         spin_lock_init(&ctx->cs_lock);
119         atomic_set(&ctx->thread_ctx_switch_token, 1);
120         ctx->thread_ctx_switch_wait_token = 0;
121
122         if (is_kernel_ctx) {
123                 ctx->asid = HL_KERNEL_ASID_ID; /* KMD gets ASID 0 */
124                 rc = hl_mmu_ctx_init(ctx);
125                 if (rc) {
126                         dev_err(hdev->dev, "Failed to init mmu ctx module\n");
127                         goto mem_ctx_err;
128                 }
129         } else {
130                 ctx->asid = hl_asid_alloc(hdev);
131                 if (!ctx->asid) {
132                         dev_err(hdev->dev, "No free ASID, failed to create context\n");
133                         return -ENOMEM;
134                 }
135
136                 rc = hl_vm_ctx_init(ctx);
137                 if (rc) {
138                         dev_err(hdev->dev, "Failed to init mem ctx module\n");
139                         rc = -ENOMEM;
140                         goto mem_ctx_err;
141                 }
142         }
143
144         return 0;
145
146 mem_ctx_err:
147         if (ctx->asid != HL_KERNEL_ASID_ID)
148                 hl_asid_free(hdev, ctx->asid);
149
150         return rc;
151 }
152
153 void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx)
154 {
155         kref_get(&ctx->refcount);
156 }
157
158 int hl_ctx_put(struct hl_ctx *ctx)
159 {
160         return kref_put(&ctx->refcount, hl_ctx_do_release);
161 }
162
163 struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq)
164 {
165         struct hl_device *hdev = ctx->hdev;
166         struct dma_fence *fence;
167
168         spin_lock(&ctx->cs_lock);
169
170         if (seq >= ctx->cs_sequence) {
171                 dev_notice(hdev->dev,
172                         "Can't wait on seq %llu because current CS is at seq %llu\n",
173                         seq, ctx->cs_sequence);
174                 spin_unlock(&ctx->cs_lock);
175                 return ERR_PTR(-EINVAL);
176         }
177
178
179         if (seq + HL_MAX_PENDING_CS < ctx->cs_sequence) {
180                 dev_dbg(hdev->dev,
181                         "Can't wait on seq %llu because current CS is at seq %llu (Fence is gone)\n",
182                         seq, ctx->cs_sequence);
183                 spin_unlock(&ctx->cs_lock);
184                 return NULL;
185         }
186
187         fence = dma_fence_get(
188                         ctx->cs_pending[seq & (HL_MAX_PENDING_CS - 1)]);
189         spin_unlock(&ctx->cs_lock);
190
191         return fence;
192 }
193
194 /*
195  * hl_ctx_mgr_init - initialize the context manager
196  *
197  * @mgr: pointer to context manager structure
198  *
199  * This manager is an object inside the hpriv object of the user process.
200  * The function is called when a user process opens the FD.
201  */
202 void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr)
203 {
204         mutex_init(&mgr->ctx_lock);
205         idr_init(&mgr->ctx_handles);
206 }
207
208 /*
209  * hl_ctx_mgr_fini - finalize the context manager
210  *
211  * @hdev: pointer to device structure
212  * @mgr: pointer to context manager structure
213  *
214  * This function goes over all the contexts in the manager and frees them.
215  * It is called when a process closes the FD.
216  */
217 void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr)
218 {
219         struct hl_ctx *ctx;
220         struct idr *idp;
221         u32 id;
222
223         idp = &mgr->ctx_handles;
224
225         idr_for_each_entry(idp, ctx, id)
226                 hl_ctx_free(hdev, ctx);
227
228         idr_destroy(&mgr->ctx_handles);
229         mutex_destroy(&mgr->ctx_lock);
230 }