Skip to content

[FEATURE] STM32: detect DMA stream oversubscription at build time #20244

Description

@dakejahl

Is your feature request related to a problem? Please describe.

On STM32H7, stm32_dmachannel() hands out the first free stream on the requested controller and returns NULL when none are left. The DMAMUX allocators in common/stm32 and the H5 GPDMA driver work the same way. Nothing checks at build time how many streams a configuration can hold at once, so a board that enables DMA on more peripherals than a controller has streams builds cleanly and fails at run time, depending on start order:

  • Of the serial drivers on these parts, only H5's checks the handle (it returns -EBUSY). On H7, up_dma_setup() passes it straight to stm32_dmasetup(), which dereferences it.
  • The H7 SPI driver only DEBUGASSERTs the handle. Its comment says stm32_dmachannel() blocks until a stream is free, which is F4/F7 behavior.
  • Whichever driver asks last loses. A UART that is opened only in some configurations (a telemetry or GPS port) can take the last stream and break a driver that worked on the bench.

Downstream in PX4 this has shown up as a board boot-looping with DMA on three UARTs, and as RC input dying once DShot telemetry took a stream (PX4/PX4-Autopilot#26112, PX4/PX4-Autopilot#24573, PX4/PX4-Autopilot#24920). Each time, the fix was turning off UART DMA by hand. Boards track the budget in comments, and the comments drift.

Describe the solution you'd like

  1. A compile-time budget per DMA controller. Every input is already an integer macro: the CONFIG_* DMA enables, the board's DMAMAP_* values, and DMAMAP_CONTROLLER(). A per-family header can sum the streams each enabled driver holds (serial RX/TX, SPI RX/TX, ADC, DAC, QSPI) per controller and #error when a sum exceeds the controller's stream count. No runtime cost and no API change.
  2. A board.h hook, e.g. BOARD_DMA1_NRESERVED, for streams allocated by board or out-of-tree code, and the per-controller sums exported as macros so out-of-tree code can add its own consumers to the check. PX4 allocates DMA outside NuttX for DShot and for its IO coprocessor link.
  3. Drivers return -EBUSY from setup when stm32_dmachannel() returns NULL, as the H5 serial driver already does, instead of dereferencing it. This is independent of 1 and 2 and can land first.

The count is conservative: a UART with DMA enabled counts even if nothing opens it, because whether it opens is usually runtime configuration. A board that deliberately overcommits needs an opt-out, so a Kconfig option could make the check a warning instead of an error.

I can do the H7 implementation if this direction is acceptable. The same approach applies to the other DMAMUX families and would fit in arch/arm/src/common/stm32 alongside the STM32 port unification (#19004).

Describe alternatives you've considered

  • Recording the owner in stm32_dmachannel() and dumping per-controller usage through procfs. Useful for debugging, but it still finds the problem only at run time.
  • Encoding a fixed stream in DMAMAP_* on DMAMUX parts, as on F4/F7. This makes allocation deterministic, but changes the map encoding and the allocator, and still needs a check that no two consumers share a stream.
  • Falling back to interrupt-driven I/O when allocation fails. The H7 serial DMA ops assume a valid handle and the per-port DMA channel fields are const, so this is a larger change than failing the open.

Verification

  • I have verified before submitting the report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions