DF-4196 / fix.diff
--- a/sys/dev/drm/drm_vblank.c +++ b/sys/dev/drm/drm_vblank.c @@ -590,6 +590,7 @@ struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; int vpos, hpos, i; int delta_ns, duration_ns; + int crtc_clock, crtc_htotal; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return false; @@ -612,10 +613,20 @@ else mode = &crtc->hwmode; + /* + * Snapshot the mode timings into locals: drm_crtc_vblank_off() clears + * vblank->hwmode.crtc_clock after releasing event_lock/vbl_lock, so a + * re-load of mode->crtc_clock between the zero check below and the + * division further down (the opaque ->get_scanout_position() call in + * between forces a reload) could divide by zero and panic the kernel. + */ + crtc_clock = mode->crtc_clock; + crtc_htotal = mode->crtc_htotal; + /* If mode timing undefined, just return as no-op: * Happens during initial modesetting of a crtc. */ - if (mode->crtc_clock == 0) { + if (crtc_clock == 0) { DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev)); @@ -668,8 +679,8 @@ * since start of scanout at first display scanline. delta_ns * can be negative if start of scanout hasn't happened yet. */ - delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), - mode->crtc_clock); + delta_ns = div_s64(1000000LL * (vpos * crtc_htotal + hpos), + crtc_clock); /* Subtract time delta from raw timestamp to get final * vblank_time timestamp for end of vblank. |