• Ben Avison's avatar
    Implement interrupt-driven and background transfer support · 81c583d7
    Ben Avison authored
    The operation state machine is now polled both when the HAL device's specified
    interrupt fires, and also on TickerV. TickerV is the minimum requirement to
    enable background transfers, since some state changes (not least timeouts)
    won't generate a device interrupt, so would otherwise never complete.
    
    Some existing implementations of HAL devices don't specify the interrupt
    number correctly. If we were to enable interrupts in the SD controller for
    these, we wouldn't be acking the correct interrupt in the interrupt
    controller. The kernel's default interrupt handler traditionally disabled
    unhandled interrupt sources, but this no longer seems to work and we end up in
    an infinite loop as kernel interrupt dispatch unwinds with the interrupt still
    pending. Even if this were fixed, failing to ack a genuine interrupt would
    still be undesirable behaviour in the case of shared interrupt lines, because
    then the other interrupts sharing the line would be collateral damage.
    
    To avoid this pitfall in the case where a new SDIODriver is loaded on an old
    HAL, we check for a new minor HAL device version number which indicates the
    interrupt number is known to be good. If the version number is too old, we
    implement background transfers using TickerV alone. Be warned that this can be
    quite slow - each operation wil typically then take 10 ms or longer.
    
    To improve foreground transfer performance in these cases, or with certain
    controllers (such as the BCM2835 "SDHOST" controller) that commonly change
    state without generating an interrupt, we also retain a poll loop during
    foreground transfers. To avoid a race condition between this poll loop and
    interrupt dispatch, which can cause deadlock in the uniprocessor case or an
    SMP case where the foreground transfer is being performed on the same core as
    the interrupt handler, the `doing_poll` mutex is upgraded to a spinrw write
    lock. Because this lock type disables IRQs on the local core, this in turn has
    an impact on interrupt latency during a foreground operation. To partially
    address this, the rate of polling is throttled (to an arbitrarily chosen
    100,000 polls per second) rather than just being as fast as possible, as
    previously.
    
    Add a test program (implemented as a module to *RMRun) to measure the
    comparative speed of an example SD command issued as both foreground and
    background operations. To ensure this is safe for all devices, the command
    used is CMD0_GO_IDLE_STATE - but note that this will leave the device in
    question dormant, so you probably want to execute it from a non-SDFS filing
    system. Example timings for a Raspberry Pi driving the memory card bus look
    like this:
    
    SDIODriver  HAL  Controller  Foreground frequency  Background frequency
    Old         Any  SDHCI       ~57,000               0 (never completes)
    New         Old  SDHCI       ~37,000               100
    New         New  SDHCI       ~44,000               ~46,000
    New         New  SDHOST      ~63,000               100
    81c583d7