A sync-only job is meant to provide a synchronization point on a queue, so we can't return a NULL fence there, we have to add a signal operation to the command stream which executes after all other previously submitted jobs are done. v2: - Fixed a UAF bug - Added R-bs
Unified · Split
| @@ -458,6 +458,16 @@ struct panthor_queue { · 457 unmodified lines | ||
| 458 | 458 | /** @seqno: Sequence number of the last initialized fence. */ |
| 459 | 459 | atomic64_t seqno; |
| 460 | 460 | |
| 461 | + /** | |
| 462 | + * @last_fence: Fence of the last submitted job. | |
| 463 | + * | |
| 464 | + * We return this fence when we get an empty command stream. | |
| 465 | + * This way, we are guaranteed that all earlier jobs have completed | |
| 466 | + * when drm_sched_job::s_fence::finished without having to feed | |
| 467 | + * the CS ring buffer with a dummy job that only signals the fence. | |
| 468 | + */ | |
| 469 | + struct dma_fence *last_fence; | |
| 470 | + | |
| 461 | 471 | /** |
| 462 | 472 | * @in_flight_jobs: List containing all in-flight jobs. |
| 463 | 473 | * |
| @@ -829,6 +839,9 @@ static void group_free_queue(struct panthor_group *group, struct panthor_queue * · 365 unmodified lines | ||
| 829 | 839 | panthor_kernel_bo_destroy(queue->ringbuf); |
| 830 | 840 | panthor_kernel_bo_destroy(queue->iface.mem); |
| 831 | 841 | |
| 842 | + /* Release the last_fence we were holding, if any. */ | |
| 843 | + dma_fence_put(queue->fence_ctx.last_fence); | |
| 844 | + | |
| 832 | 845 | kfree(queue); |
| 833 | 846 | } |
| 834 | 847 | |
| @@ -2784,9 +2797,6 @@ static void group_sync_upd_work(struct work_struct *work) · 1949 unmodified lines | ||
| 2784 | 2797 | |
| 2785 | 2798 | spin_lock(&queue->fence_ctx.lock); |
| 2786 | 2799 | list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { |
| 2787 | − if (!job->call_info.size) | |
| 2788 | − continue; | |
| 2789 | − | |
| 2790 | 2800 | if (syncobj->seqno < job->done_fence->seqno) |
| 2791 | 2801 | break; |
| 2792 | 2802 | |
| @@ -2865,11 +2875,14 @@ queue_run_job(struct drm_sched_job *sched_job) · 72 unmodified lines | ||
| 2865 | 2875 | static_assert(sizeof(call_instrs) % 64 == 0, |
| 2866 | 2876 | "call_instrs is not aligned on a cacheline"); |
| 2867 | 2877 | |
| 2868 | − /* Stream size is zero, nothing to do => return a NULL fence and let | |
| 2869 | − * drm_sched signal the parent. | |
| 2878 | + /* Stream size is zero, nothing to do except making sure all previously | |
| 2879 | + * submitted jobs are done before we signal the | |
| 2880 | + * drm_sched_job::s_fence::finished fence. | |
| 2870 | 2881 | */ |
| 2871 | − if (!job->call_info.size) | |
| 2872 | − return NULL; | |
| 2882 | + if (!job->call_info.size) { | |
| 2883 | + job->done_fence = dma_fence_get(queue->fence_ctx.last_fence); | |
| 2884 | + return dma_fence_get(job->done_fence); | |
| 2885 | + } | |
| 2873 | 2886 | |
| 2874 | 2887 | ret = pm_runtime_resume_and_get(ptdev->base.dev); |
| 2875 | 2888 | if (drm_WARN_ON(&ptdev->base, ret)) |
| @@ -2928,6 +2941,10 @@ queue_run_job(struct drm_sched_job *sched_job) · 52 unmodified lines | ||
| 2928 | 2941 | } |
| 2929 | 2942 | } |
| 2930 | 2943 | |
| 2944 | + /* Update the last fence. */ | |
| 2945 | + dma_fence_put(queue->fence_ctx.last_fence); | |
| 2946 | + queue->fence_ctx.last_fence = dma_fence_get(job->done_fence); | |
| 2947 | + | |
| 2931 | 2948 | done_fence = dma_fence_get(job->done_fence); |
| 2932 | 2949 | |
| 2933 | 2950 | out_unlock: |
| @@ -3378,10 +3395,15 @@ panthor_job_create(struct panthor_file *pfile, · 444 unmodified lines | ||
| 3378 | 3395 | goto err_put_job; |
| 3379 | 3396 | } |
| 3380 | 3397 | |
| 3381 | − job->done_fence = kzalloc(sizeof(*job->done_fence), GFP_KERNEL); | |
| 3382 | − if (!job->done_fence) { | |
| 3383 | − ret = -ENOMEM; | |
| 3384 | − goto err_put_job; | |
| 3398 | + /* Empty command streams don't need a fence, they'll pick the one from | |
| 3399 | + * the previously submitted job. | |
| 3400 | + */ | |
| 3401 | + if (job->call_info.size) { | |
| 3402 | + job->done_fence = kzalloc(sizeof(*job->done_fence), GFP_KERNEL); | |
| 3403 | + if (!job->done_fence) { | |
| 3404 | + ret = -ENOMEM; | |
| 3405 | + goto err_put_job; | |
| 3406 | + } | |
| 3385 | 3407 | } |
| 3386 | 3408 | |
| 3387 | 3409 | ret = drm_sched_job_init(&job->base, |
| @@ -802,6 +802,9 @@ struct drm_panthor_queue_submit { · 801 unmodified lines | ||
| 802 | 802 | * Must be 64-bit/8-byte aligned (the size of a CS instruction) |
| 803 | 803 | * |
| 804 | 804 | * Can be zero if stream_addr is zero too. |
| 805 | + * | |
| 806 | + * When the stream size is zero, the queue submit serves as a | |
| 807 | + * synchronization point. | |
| 805 | 808 | */ |
| 806 | 809 | __u32 stream_size; |
| 807 | 810 | |
| @@ -822,6 +825,8 @@ struct drm_panthor_queue_submit { · 14 unmodified lines | ||
| 822 | 825 | * ensure the GPU doesn't get garbage when reading the indirect command |
| 823 | 826 | * stream buffers. If you want the cache flush to happen |
| 824 | 827 | * unconditionally, pass a zero here. |
| 828 | + * | |
| 829 | + * Ignored when stream_size is zero. | |
| 825 | 830 | */ |
| 826 | 831 | __u32 latest_flush; |
| 827 | 832 | |