This way, and in imitation of Panthor, only a single reset thread can progress at a time, and other threads wanting to trigger it just wait on the ongoing one to finish and wake them up.
Signed-off-by: Adrián Larumbe <[email protected]> --- drivers/gpu/drm/panfrost/panfrost_device.c | 1 + drivers/gpu/drm/panfrost/panfrost_device.h | 5 +++-- drivers/gpu/drm/panfrost/panfrost_job.c | 16 ++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index d68f1ec82895..cf115db9cebf 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -8,6 +8,7 @@ #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/wait.h> #include "panfrost_device.h" #include "panfrost_devfreq.h" diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 6bec8fcb701e..23dea923bd04 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -167,6 +167,7 @@ struct panfrost_device { struct { struct workqueue_struct *wq; struct work_struct work; + wait_queue_head_t wait; atomic_t pending; } reset; @@ -341,8 +342,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev, static inline void panfrost_device_schedule_reset(struct panfrost_device *pfdev) { - atomic_set(&pfdev->reset.pending, 1); - queue_work(pfdev->reset.wq, &pfdev->reset.work); + if (!atomic_cmpxchg(&pfdev->reset.pending, 0, 1)) + queue_work(pfdev->reset.wq, &pfdev->reset.work); } #endif diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 2d12b83e900a..c2a1670a74e8 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -497,10 +497,8 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev, pm_runtime_put_autosuspend(pfdev->base.dev); - if (panfrost_exception_needs_reset(pfdev, js_status)) { - atomic_set(&pfdev->reset.pending, 1); + if (panfrost_exception_needs_reset(pfdev, js_status)) drm_sched_fault(&pfdev->js->queue[js].sched); - } } static void panfrost_jm_handle_done(struct panfrost_device *pfdev, @@ -755,6 +753,8 @@ panfrost_reset(struct panfrost_device *pfdev, panfrost_jm_enable_interrupts(pfdev); dma_fence_end_signalling(cookie); + + wake_up_all(&pfdev->reset.wait); } static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job @@ -763,6 +763,7 @@ static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job struct panfrost_job *job = to_panfrost_job(sched_job); struct panfrost_device *pfdev = job->pfdev; int js = panfrost_job_get_slot(job); + int ret; /* * If the GPU managed to complete this jobs fence, the timeout has @@ -797,10 +798,12 @@ static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job panfrost_core_dump(job); - atomic_set(&pfdev->reset.pending, 1); - panfrost_reset(pfdev, sched_job); + panfrost_device_schedule_reset(pfdev); + ret = wait_event_timeout(pfdev->reset.wait, + !atomic_read(&pfdev->reset.pending), + msecs_to_jiffies(60)); - return DRM_GPU_SCHED_STAT_RESET; + return (ret) ? DRM_GPU_SCHED_STAT_RESET : DRM_GPU_SCHED_STAT_ENODEV; } static void panfrost_reset_work(struct work_struct *work) @@ -892,6 +895,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev) if (!pfdev->reset.wq) return -ENOMEM; args.timeout_wq = pfdev->reset.wq; + init_waitqueue_head(&pfdev->reset.wait); for (j = 0; j < NUM_JOB_SLOTS; j++) { js->queue[j].fence_context = dma_fence_context_alloc(1); -- 2.55.0
