Spi/SpiDma: move some fields into global state#5259
Spi/SpiDma: move some fields into global state#5259bugadani wants to merge 3 commits intoesp-rs:mainfrom
Conversation
|
/test-size |
Binary Size Report
|
ea449a8 to
9afed61
Compare
|
New commits in main has made this PR unmergable. Please resolve the conflicts. |
|
/test-size loopback_dma_psram |
|
Triggered binary size analysis for #5259. Results will be posted as a comment when ready. |
There was a problem hiding this comment.
Pull request overview
This PR refactors the SPI master (Spi/SpiDma) drivers to move some per-instance fields (pin guards and DMA transfer-in-progress flags) into per-peripheral global state, reducing per-driver storage and centralizing cleanup.
Changes:
- Introduces
SpiWrapperand movesPeripheralGuardownership into it, with drop-time deinit of shared state. - Moves SPI pin guards into the per-peripheral
State(viaRefCell<SpiPinGuard>), and updateswith_*pin assignment APIs to mutate that shared state. - Moves DMA “transfer in progress” flags into a per-peripheral global DMA
State, and reworksSpiDmato ownSpiinstead ofAnySpi+ local flags.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
esp-hal/src/spi/master/mod.rs |
Moves SPI pin guard tracking into the global per-peripheral State and adds SpiWrapper with drop-time deinit. |
esp-hal/src/spi/master/dma.rs |
Refactors SpiDma to wrap Spi, and moves DMA transfer flags into per-peripheral global DMA state. |
| pub struct State { | ||
| waker: AtomicWaker, | ||
| pins: RefCell<SpiPinGuard>, | ||
|
|
There was a problem hiding this comment.
Storing SpiPinGuard inside a global State behind RefCell means pin connect/disconnect now relies on interior mutability without any explicit critical-section/mutex protection. Given this is global state for a peripheral, it would be safer to use a lock (e.g. NonReentrantMutex<SpiPinGuard>) rather than RefCell, so pin updates are explicitly serialized and don't depend on an unsafe Sync impl.
| struct State { | ||
| tx_transfer_in_progress: Cell<bool>, | ||
| rx_transfer_in_progress: Cell<bool>, | ||
| } | ||
|
|
||
| // SAFETY: State belongs to the currently constructed driver instance. As such, it'll not be | ||
| // accessed concurrently in multiple threads. | ||
| unsafe impl Sync for State {} |
There was a problem hiding this comment.
unsafe impl Sync for State is not justified here: this DMA State is stored in a static and uses Cell<bool> fields. Cell is not thread-safe and on multi-core chips it can be accessed from multiple contexts if the driver is shared, leading to potential data races/UB. Consider using AtomicBool (or portable_atomic::AtomicBool) for these flags, or wrap the state in an appropriate mutex/critical-section type and avoid the unsafe Sync impl.
| struct Info { | ||
| dma_peripheral: crate::dma::DmaPeripheral, | ||
| } | ||
| struct State { | ||
| tx_transfer_in_progress: Cell<bool>, | ||
| rx_transfer_in_progress: Cell<bool>, | ||
| } |
There was a problem hiding this comment.
The DMA helper types struct Info / struct State introduced here shadow super::Info / super::State imported via use super::*;, which makes the module harder to follow and increases the risk of accidental type confusion later. Consider renaming these to something more specific (e.g. DmaInfo/DmaState) or avoiding the wildcard import.
| unsafe impl Sync for State {} | ||
|
|
There was a problem hiding this comment.
State is made Sync via unsafe impl Sync for State, but it contains RefCell<SpiPinGuard> (and on esp32 also Cell). RefCell/Cell are not thread-safe; on multi-core targets or with interrupt preemption this Sync impl can permit data races/UB if the state is ever accessed from more than one execution context. Prefer a synchronization primitive for pins (e.g. esp_sync::NonReentrantMutex or critical_section::Mutex<RefCell<_>>), or otherwise restructure so State does not need an unsafe Sync impl.
| unsafe impl Sync for State {} |
No description provided.