• Mike Isely's avatar
    drm: Fix race that can lockup the kernel · 9df5808c
    Mike Isely authored
    The i915_vblank_swap() function schedules an automatic buffer swap
    upon receipt of the vertical sync interrupt.  Such an operation is
    lengthy so it can't be allowed to happen in normal interrupt context,
    thus the DRM implements this by scheduling the work in a kernel
    softirq-scheduled tasklet.  In order for the buffer swap to work
    safely, the DRM's central lock must be taken, via a call to
    drm_lock_take() located in drivers/char/drm/drm_irq.c within the
    function drm_locked_tasklet_func().  The lock-taking logic uses a
    non-interrupt-blocking spinlock to implement the manipulations needed
    to take the lock.  This semantic would be safe if all attempts to use
    the spinlock only happen from process context.  However this buffer
    swap happens from softirq context which is really a form of interrupt
    context.  Thus we have an unsafe situation, in that
    drm_locked_tasklet_func() can block on a spinlock already taken by a
    thread in process context which will never get scheduled again because
    of the blocked softirq tasklet.  This wedges the kernel hard.
    
    To trigger this bug, run a dual-head cloned mode configuration which
    uses the i915 drm, then execute an opengl application which
    synchronizes buffer swaps against the vertical sync interrupt.  In my
    testing, a lockup always results after running anywhere from 5 minutes
    to an hour and a half.  I believe dual-head is needed to really
    trigger the problem because then the vertical sync interrupt handling
    is no longer predictable (due to being interrupt-sourced from two
    different heads running at different speeds).  This raises the
    probability of the tasklet trying to run while the userspace DRI is
    doing things to the GPU (and manipulating the DRM lock).
    
    The fix is to change the relevant spinlock semantics to be the
    interrupt-blocking form.  After this change I am no longer able to
    trigger the lockup; the longest test run so far was 20 hours (test
    stopped after that point).
    
    Note: I have examined the places where this spinlock is being
    employed; all are reasonably short bounded sequences and should be
    suitable for interrupts being blocked without impacting overall kernel
    interrupt response latency.
    Signed-off-by: default avatarMike Isely <isely@pobox.com>
    Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
    9df5808c
drm_fops.c 11.8 KB