Skip to content
  • Al Cooper's avatar
    8250: Fix race condition in serial8250_backup_timeout(). · dbb3b1ca
    Al Cooper authored
    
    
    This is to fix an issue where output will suddenly become very slow.
    The problem occurs on 8250 UARTS with the hardware bug UART_BUG_THRE.
    
    BACKGROUND
    For normal UARTs (without UART_BUG_THRE): When the serial core layer
    gets new transmit data and the transmitter is idle, it buffers the
    data and calls the 8250s' serial8250_start_tx() routine which will
    simply enable the TX interrupt in the IER register and return. This
    should immediately fire a THRE interrupt and begin transmitting the
    data.
    For buggy UARTs (with UART_BUG_THRE): merely enabling the TX interrupt
    in IER does not necessarily generate a new THRE interrupt.
    Therefore, a background timer periodically checks to see if there is
    pending data, and starts transmission if that is the case.
    
    The bug happens on SMP systems when the system has nothing to transmit,
    the transmit interrupt is disabled and the following sequence occurs:
    - CPU0: The background timer routine serial8250_backup_timeout()
      starts and saves the state of the interrupt enable register (IER)
      and then disables all interrupts in IER. NOTE: The transmit interrupt
      (TI) bit is saved as disabled.
    - CPU1: The serial core gets data to transmit, grabs the port lock and
      calls serial8250_start_tx() which enables the TI in IER.
    - CPU0: serial8250_backup_timeout() waits for the port lock.
    - CPU1: finishes (with TI enabled) and releases the port lock.
    - CPU0: serial8250_backup_timeout() calls the interrupt routine which
      will transmit the next fifo's worth of data and then restores the
      IER from the previously saved value (TI disabled).
    At this point, as long as the serial core has more transmit data
    buffered, it will not call serial8250_start_tx() again and the
    background timer routine will slowly transmit the data.
    
    The fix is to have serial8250_start_tx() get the port lock before
    it saves the IER state and release it after restoring IER. This will
    prevent serial8250_start_tx() from running in parallel.
    
    Signed-off-by: default avatarAl Cooper <alcooperx@gmail.com>
    Cc: stable <stable@kernel.org>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
    dbb3b1ca