Skip to content

Commit d6471ad

Browse files
rfvirgilgregkh
authored andcommitted
ASoC: SDCA: Fix cleanup inversion in class driver
[ Upstream commit 7936490 ] Fix inverted cleanup of the SoundWire IRQ and the function drivers that use it. The devm cleanup function to call sdca_dev_unregister_functions() was being registered at the end of class_sdw_probe(). The bus core creates the parent SoundWire IRQ handler after class_sdw_probe() has returned, and it registers a devm cleanup handler at the same time. This led to a cleanup inversion where the devm cleanup for the parent Soundwire IRQ runs before the handler that removes the function drivers. So the parent IRQ is destroyed before the function drivers had a chance to do any cleanup and remove their IRQ handlers. Move the registrations of the function driver cleanup into class_boot_work() after the function drivers are registered, so that it runs before the cleanup of the parent SoundWire IRQ handler. Fixes: 2d877d0 ("ASoC: SDCA: Add basic SDCA class driver") Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20260409164328.3999434-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 6688b92 commit d6471ad

1 file changed

Lines changed: 21 additions & 13 deletions

File tree

sound/soc/sdca/sdca_class.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ static const struct regmap_config class_dev_regmap_config = {
137137
.unlock = class_regmap_unlock,
138138
};
139139

140+
static void class_remove_functions(void *data)
141+
{
142+
struct sdca_class_drv *drv = data;
143+
144+
sdca_dev_unregister_functions(drv->sdw);
145+
}
146+
140147
static void class_boot_work(struct work_struct *work)
141148
{
142149
struct sdca_class_drv *drv = container_of(work,
@@ -157,6 +164,11 @@ static void class_boot_work(struct work_struct *work)
157164
if (ret)
158165
goto err;
159166

167+
/* Ensure function drivers are removed before the IRQ is destroyed */
168+
ret = devm_add_action_or_reset(drv->dev, class_remove_functions, drv);
169+
if (ret)
170+
goto err;
171+
160172
dev_dbg(drv->dev, "boot work complete\n");
161173

162174
pm_runtime_mark_last_busy(drv->dev);
@@ -168,15 +180,6 @@ static void class_boot_work(struct work_struct *work)
168180
pm_runtime_put_sync(drv->dev);
169181
}
170182

171-
static void class_dev_remove(void *data)
172-
{
173-
struct sdca_class_drv *drv = data;
174-
175-
cancel_work_sync(&drv->boot_work);
176-
177-
sdca_dev_unregister_functions(drv->sdw);
178-
}
179-
180183
static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
181184
{
182185
struct device *dev = &sdw->dev;
@@ -230,15 +233,19 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
230233
if (ret)
231234
return ret;
232235

233-
ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
234-
if (ret)
235-
return ret;
236-
237236
queue_work(system_long_wq, &drv->boot_work);
238237

239238
return 0;
240239
}
241240

241+
static void class_sdw_remove(struct sdw_slave *sdw)
242+
{
243+
struct device *dev = &sdw->dev;
244+
struct sdca_class_drv *drv = dev_get_drvdata(dev);
245+
246+
cancel_work_sync(&drv->boot_work);
247+
}
248+
242249
static int class_suspend(struct device *dev)
243250
{
244251
struct sdca_class_drv *drv = dev_get_drvdata(dev);
@@ -328,6 +335,7 @@ static struct sdw_driver class_sdw_driver = {
328335
},
329336

330337
.probe = class_sdw_probe,
338+
.remove = class_sdw_remove,
331339
.id_table = class_sdw_id,
332340
.ops = &class_sdw_ops,
333341
};

0 commit comments

Comments
 (0)