• Sarah Sharp's avatar
    USB: xhci: Handle URB cancel, complete and resubmit race. · 678539cf
    Sarah Sharp authored
    In the old code, there was a race condition between the stop endpoint
    command and the URB submission process.  When the stop endpoint command is
    handled by the event handler, the endpoint ring is assumed to be stopped.
    When a stop endpoint command is queued, URB submissions are to not ring
    the doorbell.  The old code would check the number of pending URBs to be
    canceled, and would not ring the doorbell if it was non-zero.
    
    However, the following race condition could occur with the old code:
    
    1. Cancel an URB, add it to the list of URBs to be canceled, queue the stop
       endpoint command, and increment ep->cancels_pending to 1.
    2. The URB finishes on the HW, and an event is enqueued to the event ring
       (at the same time as 1).
    3. The stop endpoint command finishes, and the endpoint is halted.  An
       event is queued to the event ring.
    4. The event handler sees the finished URB, notices it was to be
       canceled, decrements ep->cancels_pending to 0, and removes it from the to
       be canceled list.
    5. The event handler drops the lock and gives back the URB.  The
       completion handler requeues the URB (or a different driver enqueues a new
       URB).  This causes the endpoint's doorbell to be rung, since
       ep->cancels_pending == 0.  The endpoint is now running.
    6. A second URB is canceled, and it's added to the canceled list.
       Since ep->cancels_pending == 0, a new stop endpoint command is queued, and
       ep->cancels_pending is incremented to 1.
    7. The event handler then sees the completed stop endpoint command.  The
       handler assumes the endpoint is stopped, but it isn't.  It attempts to
       move the dequeue pointer or change TDs to cancel the second URB, while the
       hardware is actively accessing the endpoint ring.
    
    To eliminate this race condition, a new endpoint state bit is introduced,
    EP_HALT_PENDING.  When this bit is set, a stop endpoint command has been
    queued, and the command handler has not begun to process the URB
    cancellation list yet.  The endpoint doorbell should not be rung when this
    is set.  Set this when a stop endpoint command is queued, clear it when
    the handler for that command runs, and check if it's set before ringing a
    doorbell.  ep->cancels_pending is eliminated, because it is no longer
    used.
    
    Make sure to ring the doorbell for an endpoint when the stop endpoint
    command handler runs, even if the canceled URB list is empty.  All
    canceled URBs could have completed and new URBs could have been enqueued
    without the doorbell being rung before the command was handled.
    Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
    678539cf
xhci.h 43.9 KB