DF-4182 / fix.diff
fix for DF-4182: radeon_flip_work_func programs a page flip and sets flip_status = RADEON_FLIP_SUBMITTED even when the CRTC was disabled while the flip was pending. A flip on a disabled CRTC never completes (its vblank/pflip interrupts are off), and nothing else in the driver ever resets flip_status, so the CRTC's page-flip path is wedged forever (-EBUSY for every subsequent flip), the queued vblank event is never delivered, and the drm_crtc_vblank_get reference taken by the ioctl core leaks. Fix: abort the flip cleanly when the CRTC is disabled at programming time: reset flip_status/flip_work, drop the vblank reference, deliver the event, unpin the new BO (kept in struct radeon_flip_work for the abort path) and queue the existing unpin work. Files: sys/dev/drm/radeon/radeon.h, sys/dev/drm/radeon/radeon_display.c --- a/sys/dev/drm/radeon/radeon.h +++ b/sys/dev/drm/radeon/radeon.h @@ -750,6 +750,7 @@ uint64_t base; struct drm_pending_vblank_event *event; struct radeon_bo *old_rbo; + struct radeon_bo *new_rbo; /* pinned by page_flip_target, unpinned on abort */ struct dma_fence *fence; bool async; }; --- a/sys/dev/drm/radeon/radeon_display.c +++ b/sys/dev/drm/radeon/radeon_display.c @@ -460,6 +460,30 @@ /* We borrow the event spin lock for protecting flip_status */ spin_lock_irqsave(&crtc->dev->event_lock, flags); + /* + * If the CRTC was disabled while the flip was pending, abort it: + * a flip programmed on a disabled CRTC never completes, leaving + * flip_status == RADEON_FLIP_SUBMITTED forever, which wedges every + * future page flip on this CRTC with -EBUSY and leaks the vblank + * reference taken by the ioctl core. + */ + if (!radeon_crtc->enabled) { + radeon_crtc->flip_status = RADEON_FLIP_NONE; + radeon_crtc->flip_work = NULL; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + up_read(&rdev->exclusive_lock); + drm_crtc_vblank_put(&radeon_crtc->base); + if (work->event) + drm_crtc_send_vblank_event(&radeon_crtc->base, + work->event); + if (work->new_rbo && !radeon_bo_reserve(work->new_rbo, false)) { + radeon_bo_unpin(work->new_rbo); + radeon_bo_unreserve(work->new_rbo); + } + queue_work(radeon_crtc->flip_queue, &work->unpin_work); + return; + } + /* set the proper interrupt */ radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); @@ -532,6 +556,7 @@ work->fence = dma_fence_get(reservation_object_get_excl(new_rbo->tbo.resv)); radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL); radeon_bo_unreserve(new_rbo); + work->new_rbo = new_rbo; if (!ASIC_IS_AVIVO(rdev)) { /* crtc offset is from display base addr not FB location */ |