.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
---|
2 | 2 | /* |
---|
3 | 3 | * |
---|
4 | | - * (C) COPYRIGHT 2012-2021 ARM Limited. All rights reserved. |
---|
| 4 | + * (C) COPYRIGHT 2012-2022 ARM Limited. All rights reserved. |
---|
5 | 5 | * |
---|
6 | 6 | * This program is free software and is provided to you under the terms of the |
---|
7 | 7 | * GNU General Public License version 2 as published by the Free Software |
---|
.. | .. |
---|
21 | 21 | |
---|
22 | 22 | /* |
---|
23 | 23 | * Code for supporting explicit Linux fences (CONFIG_SYNC_FILE) |
---|
24 | | - * Introduced in kernel 4.9. |
---|
25 | | - * Android explicit fences (CONFIG_SYNC) can be used for older kernels |
---|
26 | | - * (see mali_kbase_sync_android.c) |
---|
27 | 24 | */ |
---|
28 | 25 | |
---|
29 | 26 | #include <linux/sched.h> |
---|
.. | .. |
---|
112 | 109 | struct dma_fence *fence = sync_file_get_fence(fd); |
---|
113 | 110 | #endif |
---|
114 | 111 | |
---|
| 112 | + lockdep_assert_held(&katom->kctx->jctx.lock); |
---|
| 113 | + |
---|
115 | 114 | if (!fence) |
---|
116 | 115 | return -ENOENT; |
---|
117 | 116 | |
---|
118 | 117 | kbase_fence_fence_in_set(katom, fence); |
---|
| 118 | + katom->dma_fence.fence_cb_added = false; |
---|
119 | 119 | |
---|
120 | 120 | return 0; |
---|
121 | 121 | } |
---|
.. | .. |
---|
167 | 167 | struct dma_fence_cb *cb) |
---|
168 | 168 | #endif |
---|
169 | 169 | { |
---|
170 | | - struct kbase_fence_cb *kcb = container_of(cb, |
---|
171 | | - struct kbase_fence_cb, |
---|
172 | | - fence_cb); |
---|
173 | | - struct kbase_jd_atom *katom = kcb->katom; |
---|
| 170 | + struct kbase_jd_atom *katom = container_of(cb, struct kbase_jd_atom, |
---|
| 171 | + dma_fence.fence_cb); |
---|
174 | 172 | struct kbase_context *kctx = katom->kctx; |
---|
175 | 173 | |
---|
176 | 174 | /* Cancel atom if fence is erroneous */ |
---|
| 175 | + if (dma_fence_is_signaled(katom->dma_fence.fence_in) && |
---|
177 | 176 | #if (KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE || \ |
---|
178 | 177 | (KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE && \ |
---|
179 | 178 | KERNEL_VERSION(4, 9, 68) <= LINUX_VERSION_CODE)) |
---|
180 | | - if (dma_fence_is_signaled(kcb->fence) && kcb->fence->error < 0) |
---|
| 179 | + katom->dma_fence.fence_in->error < 0) |
---|
181 | 180 | #else |
---|
182 | | - if (dma_fence_is_signaled(kcb->fence) && kcb->fence->status < 0) |
---|
| 181 | + katom->dma_fence.fence_in->status < 0) |
---|
183 | 182 | #endif |
---|
184 | 183 | katom->event_code = BASE_JD_EVENT_JOB_CANCELLED; |
---|
185 | 184 | |
---|
186 | | - if (kbase_fence_dep_count_dec_and_test(katom)) { |
---|
187 | | - /* We take responsibility of handling this */ |
---|
188 | | - kbase_fence_dep_count_set(katom, -1); |
---|
189 | 185 | |
---|
190 | | - /* To prevent a potential deadlock we schedule the work onto the |
---|
191 | | - * job_done_wq workqueue |
---|
192 | | - * |
---|
193 | | - * The issue is that we may signal the timeline while holding |
---|
194 | | - * kctx->jctx.lock and the callbacks are run synchronously from |
---|
195 | | - * sync_timeline_signal. So we simply defer the work. |
---|
196 | | - */ |
---|
197 | | - INIT_WORK(&katom->work, kbase_sync_fence_wait_worker); |
---|
198 | | - queue_work(kctx->jctx.job_done_wq, &katom->work); |
---|
199 | | - } |
---|
| 186 | + /* To prevent a potential deadlock we schedule the work onto the |
---|
| 187 | + * job_done_wq workqueue |
---|
| 188 | + * |
---|
| 189 | + * The issue is that we may signal the timeline while holding |
---|
| 190 | + * kctx->jctx.lock and the callbacks are run synchronously from |
---|
| 191 | + * sync_timeline_signal. So we simply defer the work. |
---|
| 192 | + */ |
---|
| 193 | + INIT_WORK(&katom->work, kbase_sync_fence_wait_worker); |
---|
| 194 | + queue_work(kctx->jctx.job_done_wq, &katom->work); |
---|
200 | 195 | } |
---|
201 | 196 | |
---|
202 | 197 | int kbase_sync_fence_in_wait(struct kbase_jd_atom *katom) |
---|
.. | .. |
---|
208 | 203 | struct dma_fence *fence; |
---|
209 | 204 | #endif |
---|
210 | 205 | |
---|
211 | | - fence = kbase_fence_in_get(katom); |
---|
| 206 | + lockdep_assert_held(&katom->kctx->jctx.lock); |
---|
| 207 | + |
---|
| 208 | + fence = katom->dma_fence.fence_in; |
---|
212 | 209 | if (!fence) |
---|
213 | 210 | return 0; /* no input fence to wait for, good to go! */ |
---|
214 | 211 | |
---|
215 | | - kbase_fence_dep_count_set(katom, 1); |
---|
| 212 | + err = dma_fence_add_callback(fence, &katom->dma_fence.fence_cb, |
---|
| 213 | + kbase_fence_wait_callback); |
---|
| 214 | + if (err == -ENOENT) { |
---|
| 215 | + int fence_status = dma_fence_get_status(fence); |
---|
216 | 216 | |
---|
217 | | - err = kbase_fence_add_callback(katom, fence, kbase_fence_wait_callback); |
---|
218 | | - |
---|
219 | | - kbase_fence_put(fence); |
---|
220 | | - |
---|
221 | | - if (likely(!err)) { |
---|
222 | | - /* Test if the callbacks are already triggered */ |
---|
223 | | - if (kbase_fence_dep_count_dec_and_test(katom)) { |
---|
224 | | - kbase_fence_free_callbacks(katom); |
---|
225 | | - kbase_fence_dep_count_set(katom, -1); |
---|
226 | | - return 0; /* Already signaled, good to go right now */ |
---|
| 217 | + if (fence_status == 1) { |
---|
| 218 | + /* Fence is already signaled with no error. The completion |
---|
| 219 | + * for FENCE_WAIT softjob can be done right away. |
---|
| 220 | + */ |
---|
| 221 | + return 0; |
---|
227 | 222 | } |
---|
228 | 223 | |
---|
229 | | - /* Callback installed, so we just need to wait for it... */ |
---|
230 | | - } else { |
---|
231 | | - /* Failure */ |
---|
232 | | - kbase_fence_free_callbacks(katom); |
---|
233 | | - kbase_fence_dep_count_set(katom, -1); |
---|
| 224 | + /* Fence shouldn't be in not signaled state */ |
---|
| 225 | + if (!fence_status) { |
---|
| 226 | + struct kbase_sync_fence_info info; |
---|
234 | 227 | |
---|
235 | | - katom->event_code = BASE_JD_EVENT_JOB_CANCELLED; |
---|
| 228 | + kbase_sync_fence_in_info_get(katom, &info); |
---|
236 | 229 | |
---|
237 | | - /* We should cause the dependent jobs in the bag to be failed, |
---|
238 | | - * to do this we schedule the work queue to complete this job |
---|
| 230 | + dev_warn(katom->kctx->kbdev->dev, |
---|
| 231 | + "Unexpected status for fence %s of ctx:%d_%d atom:%d", |
---|
| 232 | + info.name, katom->kctx->tgid, katom->kctx->id, |
---|
| 233 | + kbase_jd_atom_id(katom->kctx, katom)); |
---|
| 234 | + } |
---|
| 235 | + |
---|
| 236 | + /* If fence is signaled with an error, then the FENCE_WAIT softjob is |
---|
| 237 | + * considered to be failed. |
---|
239 | 238 | */ |
---|
240 | | - INIT_WORK(&katom->work, kbase_sync_fence_wait_worker); |
---|
241 | | - queue_work(katom->kctx->jctx.job_done_wq, &katom->work); |
---|
242 | 239 | } |
---|
243 | 240 | |
---|
244 | | - return 1; /* completion to be done later by callback/worker */ |
---|
| 241 | + if (unlikely(err)) { |
---|
| 242 | + /* We should cause the dependent jobs in the bag to be failed. */ |
---|
| 243 | + katom->event_code = BASE_JD_EVENT_JOB_CANCELLED; |
---|
| 244 | + |
---|
| 245 | + /* The completion for FENCE_WAIT softjob can be done right away. */ |
---|
| 246 | + return 0; |
---|
| 247 | + } |
---|
| 248 | + |
---|
| 249 | + /* Callback was successfully installed */ |
---|
| 250 | + katom->dma_fence.fence_cb_added = true; |
---|
| 251 | + |
---|
| 252 | + /* Completion to be done later by callback/worker */ |
---|
| 253 | + return 1; |
---|
245 | 254 | } |
---|
246 | 255 | |
---|
247 | 256 | void kbase_sync_fence_in_cancel_wait(struct kbase_jd_atom *katom) |
---|
248 | 257 | { |
---|
249 | | - if (!kbase_fence_free_callbacks(katom)) { |
---|
250 | | - /* The wait wasn't cancelled - |
---|
251 | | - * leave the cleanup for kbase_fence_wait_callback |
---|
252 | | - */ |
---|
253 | | - return; |
---|
254 | | - } |
---|
| 258 | + lockdep_assert_held(&katom->kctx->jctx.lock); |
---|
255 | 259 | |
---|
256 | | - /* Take responsibility of completion */ |
---|
257 | | - kbase_fence_dep_count_set(katom, -1); |
---|
| 260 | + if (katom->dma_fence.fence_cb_added) { |
---|
| 261 | + if (!dma_fence_remove_callback(katom->dma_fence.fence_in, |
---|
| 262 | + &katom->dma_fence.fence_cb)) { |
---|
| 263 | + /* The callback is already removed so leave the cleanup |
---|
| 264 | + * for kbase_fence_wait_callback. |
---|
| 265 | + */ |
---|
| 266 | + return; |
---|
| 267 | + } |
---|
| 268 | + } else { |
---|
| 269 | + struct kbase_sync_fence_info info; |
---|
| 270 | + |
---|
| 271 | + kbase_sync_fence_in_info_get(katom, &info); |
---|
| 272 | + dev_warn(katom->kctx->kbdev->dev, |
---|
| 273 | + "Callback was not added earlier for fence %s of ctx:%d_%d atom:%d", |
---|
| 274 | + info.name, katom->kctx->tgid, katom->kctx->id, |
---|
| 275 | + kbase_jd_atom_id(katom->kctx, katom)); |
---|
| 276 | + } |
---|
258 | 277 | |
---|
259 | 278 | /* Wait was cancelled - zap the atoms */ |
---|
260 | 279 | katom->event_code = BASE_JD_EVENT_JOB_CANCELLED; |
---|
.. | .. |
---|
262 | 281 | kbasep_remove_waiting_soft_job(katom); |
---|
263 | 282 | kbase_finish_soft_job(katom); |
---|
264 | 283 | |
---|
265 | | - if (jd_done_nolock(katom, NULL)) |
---|
| 284 | + if (kbase_jd_done_nolock(katom, true)) |
---|
266 | 285 | kbase_js_sched_all(katom->kctx->kbdev); |
---|
267 | 286 | } |
---|
268 | 287 | |
---|
.. | .. |
---|
273 | 292 | |
---|
274 | 293 | void kbase_sync_fence_in_remove(struct kbase_jd_atom *katom) |
---|
275 | 294 | { |
---|
276 | | - kbase_fence_free_callbacks(katom); |
---|
| 295 | + lockdep_assert_held(&katom->kctx->jctx.lock); |
---|
| 296 | + |
---|
| 297 | + if (katom->dma_fence.fence_cb_added) { |
---|
| 298 | + bool removed = dma_fence_remove_callback(katom->dma_fence.fence_in, |
---|
| 299 | + &katom->dma_fence.fence_cb); |
---|
| 300 | + |
---|
| 301 | + /* Here it is expected that the callback should have already been removed |
---|
| 302 | + * previously either by kbase_sync_fence_in_cancel_wait() or when the fence |
---|
| 303 | + * was signaled and kbase_sync_fence_wait_worker() was called. |
---|
| 304 | + */ |
---|
| 305 | + if (removed) { |
---|
| 306 | + struct kbase_sync_fence_info info; |
---|
| 307 | + |
---|
| 308 | + kbase_sync_fence_in_info_get(katom, &info); |
---|
| 309 | + dev_warn(katom->kctx->kbdev->dev, |
---|
| 310 | + "Callback was not removed earlier for fence %s of ctx:%d_%d atom:%d", |
---|
| 311 | + info.name, katom->kctx->tgid, katom->kctx->id, |
---|
| 312 | + kbase_jd_atom_id(katom->kctx, katom)); |
---|
| 313 | + } |
---|
| 314 | + } |
---|
| 315 | + |
---|
277 | 316 | kbase_fence_in_remove(katom); |
---|
| 317 | + katom->dma_fence.fence_cb_added = false; |
---|
278 | 318 | } |
---|
279 | 319 | #endif /* !MALI_USE_CSF */ |
---|
280 | 320 | |
---|
.. | .. |
---|
288 | 328 | { |
---|
289 | 329 | info->fence = fence; |
---|
290 | 330 | |
---|
291 | | - /* translate into CONFIG_SYNC status: |
---|
| 331 | + /* Translate into the following status, with support for error handling: |
---|
292 | 332 | * < 0 : error |
---|
293 | 333 | * 0 : active |
---|
294 | 334 | * 1 : signaled |
---|
.. | .. |
---|
309 | 349 | info->status = 0; /* still active (unsignaled) */ |
---|
310 | 350 | } |
---|
311 | 351 | |
---|
312 | | -#if (KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE) |
---|
313 | | - scnprintf(info->name, sizeof(info->name), "%u#%u", |
---|
314 | | - fence->context, fence->seqno); |
---|
315 | | -#elif (KERNEL_VERSION(5, 1, 0) > LINUX_VERSION_CODE) |
---|
| 352 | +#if (KERNEL_VERSION(5, 1, 0) > LINUX_VERSION_CODE) |
---|
316 | 353 | scnprintf(info->name, sizeof(info->name), "%llu#%u", |
---|
317 | 354 | fence->context, fence->seqno); |
---|
318 | 355 | #else |
---|