• Mathias Nyman's avatar
    xhci: Fix lost USB 2 remote wake · 72f68bf5
    Mathias Nyman authored
    There's a small window where a USB 2 remote wake may be left unhandled
    due to a race between hub thread and xhci port event interrupt handler.
    
    When the resume event is detected in the xhci interrupt handler it kicks
    the hub timer, which should move the port from resume to U0 once resume
    has been signalled for long enough.
    
    To keep the hub "thread" running we set a bus_state->resuming_ports flag.
    This flag makes sure hub timer function kicks itself.
    
    checking this flag was not properly protected by the spinlock. Flag was
    copied to a local variable before lock was taken. The local variable was
    then checked later with spinlock held.
    
    If interrupt is handled right after copying the flag to the local variable
    we end up stopping the hub thread before it can handle the USB 2 resume.
    
    CPU0					CPU1
    (hub thread)				(xhci event handler)
    
    xhci_hub_status_data()
    status = bus_state->resuming_ports;
    					<Interrupt>
    					handle_port_status()
    					spin_lock()
    					bus_state->resuming_ports = 1
    					set_flag(HCD_FLAG_POLL_RH)
    					spin_unlock()
    spin_lock()
    if (!status)
      clear_flag(HCD_FLAG_POLL_RH)
    spin_unlock()
    
    Fix this by taking the lock a bit earlier so that it covers
    the resuming_ports flag copy in the hub thread
    
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
    Link: https://lore.kernel.org/r/20210715150651.1996099-2-mathias.nyman@linux.intel.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    72f68bf5
xhci-hub.c 55.3 KB