• David Brownell's avatar
    [PATCH] USB: EHCI qh update race fix · 6679699c
    David Brownell authored
    This makes the EHCI driver stop trying to update a live QH ... it's
    not like OHCI, that can't be done safely because of a hardware race.
    The fix:
    
        - Unlinks the QH before updating it; only the tail can safely be
          updated "live", not the queue head.  The async schedule (all
          control/bulk QHs) and periodic schedule (interrupt QH) work a
          bit differently ... high bandwidth transfers will hiccup.
    
        - Moves "update QH" and "clear toggle" logic into one new
          qh_refresh() routine, used in several places.
    
    The race shows readily enough under load with the right hardware.
    The controller silicon might be relatively slow, or maybe it's the
    bus that's slow/busy:
    
    	Host			Controller
    	---			----------
    				reads two TD pointers
    	update two TD pointers
    	wmb()
    	activate QH
    				reads rest of QH
    
    Net result is that the HC treated old TD pointers as valid, and things
    started misbehaving.  Busy controllers would misbehave worse; some systems
    wouldn't notice more than a slowdown, especially with light USB loads.
    
    This affects behavior in two cases.  The uncommon one is when an endpoint
    gets an error and halts.  The more common one happens when the controller
    runs off the end of its queue and overlays an inactive "dummy" TD into
    the QH ... something the spec says shouldn't happen, but which more
    silicon seems to be doing.  (Presumably to reduce DMA chatter.)
    Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
    Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
    6679699c
ehci-q.c 30.1 KB