From: Charles Keepax ckeepax@opensource.cirrus.com
[ Upstream commit ccb7bb13c00bcc3178d270da052635c56148bc16 ]
The sdw_dev_lock protects the SoundWire driver callbacks against the probed flag, which is used to skip the callbacks if the driver gets removed. For more information see commit bd29c00edd0a ("soundwire: revisit driver bind/unbind and callbacks").
However, this lock is a frequent source of mutex inversions. Many audio operations eventually hit the hardware resulting in a SoundWire callback, this means that typically the driver has the locking order ALSA/ASoC locks -> sdw_dev_lock. Conversely, the IRQ comes in directly from the SoundWire hardware, but then will often want to access ALSA/ASoC, such as updating something in DAPM or an ALSA control. This gives the other lock order sdw_dev_lock -> ALSA/ASoC locks.
When the IRQ handling was initially added to SoundWire this was through a callback mechanism. As such it required being covered by the lock because the callbacks are part of the sdw_driver structure and are thus present regardless of if the driver is currently probed.
Since then a newer mechanism using the IRQ framework has been added, which is currently covered by the same lock but this isn't actually required. Handlers for the IRQ framework are registered in probe and should by released during remove, thus the IRQ framework will have already unbound the IRQ before the slave driver is removed. Avoid the aforementioned mutex inversion by moving the handle_nested_irq call outside of the sdw_dev_lock.
Signed-off-by: Charles Keepax ckeepax@opensource.cirrus.com Link: https://lore.kernel.org/r/20250609143041.495049-3-ckeepax@opensource.cirrus.... Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
**Backport Status: YES**
This commit should be backported to stable kernel trees for the following reasons:
## Bug Fix Nature The commit fixes a **real deadlock/mutex inversion issue** that affects production systems. The code changes show that `handle_nested_irq()` is moved outside the `sdw_dev_lock` mutex protection (lines 1756-1757 moved before line 1759), which resolves a lock ordering problem: - **Lock order A**: ALSA/ASoC locks → sdw_dev_lock (during normal audio operations) - **Lock order B**: sdw_dev_lock → ALSA/ASoC locks (during IRQ handling)
This ABBA deadlock pattern can cause system hangs in real-world usage.
## Small and Contained Fix The actual code change is minimal - just **moving 2 lines of code** (the `handle_nested_irq()` call) from inside the mutex-protected region to outside it. This is a surgical fix that: - Only affects the IRQ handling path in `sdw_handle_slave_alerts()` - Doesn't change any logic or add new features - Has clear boundaries within the SoundWire subsystem
## Safe to Move Outside Lock The commit message explains why this is safe: The IRQ framework ensures handlers are unregistered during device removal **before** the driver is unbound. This means: 1. The IRQ handler registration happens in probe 2. The IRQ handler unregistration happens in remove 3. The `sdw_dev_lock` protects the `probed` flag and driver callbacks 4. Since the IRQ framework guarantees the handler is gone before remove completes, there's no race condition
## History Shows Lock Issues are Common The git history reveals multiple mutex/deadlock fixes in SoundWire: - `a4857d1afdd1`: Fixed deadlock with ordered master_list - `bd29c00edd0a`: Major rework of locking for bind/unbind (the commit that introduced sdw_dev_lock) - Multiple other lock-related fixes
This indicates the locking in SoundWire has been problematic and fixes are important for stability.
## Follows Stable Rules ✓ Fixes a real bug (deadlock) ✓ Minimal change (2 lines moved) ✓ No new features ✓ Low regression risk ✓ Clear explanation of why it's safe ✓ Fixes an issue introduced by earlier changes (when IRQ framework support was added in commit `12a95123bfe1`)
The commit is a textbook example of a stable-appropriate fix: it resolves a real deadlock issue with minimal code changes and clear safety justification.
drivers/soundwire/bus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 68db4b67a86f..4fd5cac799c5 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1753,15 +1753,15 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
/* Update the Slave driver */ if (slave_notify) { + if (slave->prop.use_domain_irq && slave->irq) + handle_nested_irq(slave->irq); + mutex_lock(&slave->sdw_dev_lock);
if (slave->probed) { struct device *dev = &slave->dev; struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
- if (slave->prop.use_domain_irq && slave->irq) - handle_nested_irq(slave->irq); - if (drv->ops && drv->ops->interrupt_callback) { slave_intr.sdca_cascade = sdca_cascade; slave_intr.control_port = clear;