[PATCH v2] drm/vkms: Fix race-condition between the hrtimer and the atomic commit

Arthur Grillo Queiroz Cabral arthurgrillo at riseup.net
Wed Jun 21 17:58:23 UTC 2023



On 23/05/23 09:32, Maíra Canal wrote:
> Currently, it is possible for the composer to be set as enabled and then
> as disabled without a proper call for the vkms_vblank_simulate(). This
> is problematic, because the driver would skip one CRC output, causing CRC
> tests to fail. Therefore, we need to make sure that, for each time the
> composer is set as enabled, a composer job is added to the queue.
> 
> In order to provide this guarantee, add a mutex that will lock before
> the composer is set as enabled and will unlock only after the composer
> job is added to the queue. This way, we can have a guarantee that the
> driver won't skip a CRC entry.
> 
> This race-condition is affecting the IGT test "writeback-check-output",
> making the test fail and also, leaking writeback framebuffers, as the
> writeback job is queued, but it is not signaled. This patch avoids both
> problems.
> 
> [v2]:
>     * Create a new mutex and keep the spinlock across the atomic commit in
>       order to avoid interrupts that could result in deadlocks.
> 
> Signed-off-by: Maíra Canal <mcanal at igalia.com>

Great catch!

Reviewed-by: Arthur Grillo <arthurgrillo at riseup.net>

> ---
>  drivers/gpu/drm/vkms/vkms_composer.c | 9 +++++++--
>  drivers/gpu/drm/vkms/vkms_crtc.c     | 9 +++++----
>  drivers/gpu/drm/vkms/vkms_drv.h      | 4 +++-
>  3 files changed, 15 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
> index 906d3df40cdb..b12188fd6b38 100644
> --- a/drivers/gpu/drm/vkms/vkms_composer.c
> +++ b/drivers/gpu/drm/vkms/vkms_composer.c
> @@ -320,10 +320,15 @@ void vkms_set_composer(struct vkms_output *out, bool enabled)
>  	if (enabled)
>  		drm_crtc_vblank_get(&out->crtc);
>  
> -	spin_lock_irq(&out->lock);
> +	mutex_lock(&out->enabled_lock);
>  	old_enabled = out->composer_enabled;
>  	out->composer_enabled = enabled;
> -	spin_unlock_irq(&out->lock);
> +
> +	/* the composition wasn't enabled, so unlock the lock to make sure the lock
> +	 * will be balanced even if we have a failed commit
> +	 */
> +	if (!out->composer_enabled)
> +		mutex_unlock(&out->enabled_lock);
>  
>  	if (old_enabled)
>  		drm_crtc_vblank_put(&out->crtc);
> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
> index 515f6772b866..3079013c8b32 100644
> --- a/drivers/gpu/drm/vkms/vkms_crtc.c
> +++ b/drivers/gpu/drm/vkms/vkms_crtc.c
> @@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
>  	struct drm_crtc *crtc = &output->crtc;
>  	struct vkms_crtc_state *state;
>  	u64 ret_overrun;
> -	bool ret, fence_cookie;
> +	bool ret, fence_cookie, composer_enabled;
>  
>  	fence_cookie = dma_fence_begin_signalling();
>  
> @@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
>  	if (ret_overrun != 1)
>  		pr_warn("%s: vblank timer overrun\n", __func__);
>  
> -	spin_lock(&output->lock);
>  	ret = drm_crtc_handle_vblank(crtc);
>  	if (!ret)
>  		DRM_ERROR("vkms failure on handling vblank");
>  
>  	state = output->composer_state;
> -	spin_unlock(&output->lock);
> +	composer_enabled = output->composer_enabled;
> +	mutex_unlock(&output->enabled_lock);
>  
> -	if (state && output->composer_enabled) {
> +	if (state && composer_enabled) {
>  		u64 frame = drm_crtc_accurate_vblank_count(crtc);
>  
>  		/* update frame_start only if a queued vkms_composer_worker()
> @@ -292,6 +292,7 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
>  
>  	spin_lock_init(&vkms_out->lock);
>  	spin_lock_init(&vkms_out->composer_lock);
> +	mutex_init(&vkms_out->enabled_lock);
>  
>  	vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
>  	if (!vkms_out->composer_workq)
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
> index 5f1a0a44a78c..dcf4e302fb4d 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.h
> +++ b/drivers/gpu/drm/vkms/vkms_drv.h
> @@ -100,8 +100,10 @@ struct vkms_output {
>  	struct workqueue_struct *composer_workq;
>  	/* protects concurrent access to composer */
>  	spinlock_t lock;
> +	/* guarantees that if the composer is enabled, a job will be queued */
> +	struct mutex enabled_lock;
>  
> -	/* protected by @lock */
> +	/* protected by @enabled_lock */
>  	bool composer_enabled;
>  	struct vkms_crtc_state *composer_state;
>  


More information about the dri-devel mailing list