| .. | .. |
|---|
| 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 |
|---|