public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown
@ 2026-05-05 11:18 Cássio Gabriel
  2026-05-05 11:18 ` [PATCH v4 1/2] firmware_loader: Add cancel helper for async requests Cássio Gabriel
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Cássio Gabriel @ 2026-05-05 11:18 UTC (permalink / raw)
  To: Luis Chamberlain, Russ Weight, Danilo Krummrich,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela
  Cc: driver-core, linux-kernel, linux-sound, Cássio Gabriel,
	stable

TAS2781 HDA I2C and SPI queue RCA firmware loading with
request_firmware_nowait() during component bind. The firmware loader
keeps the callback module pinned and holds a device reference, but it
does not provide a way for drivers to cancel or synchronize the queued
callback before tearing down driver-private state.

Add a small firmware-loader helper to cancel or synchronize async firmware
requests, then use it from TAS2781 HDA unbind before controls and DSP state
are removed.

No hardware runtime test was available.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
Changes in v4:
- Use spin_lock_irq() in the worker and cancel paths, which run from
  sleepable contexts.
- Fold kfree(fw_work) into firmware_work_free().
- Keep irqsave in the request path so GFP_ATOMIC callers do not depend on
  IRQ state assumptions.
- Link to v3: https://patch.msgid.link/20260501-alsa-hda-tas2781-fw-callback-teardown-v3-0-8d9f873b97bd@gmail.com

Changes in v3:
- Keep request_firmware_nowait() manually managed instead of making the
  existing API implicitly devres-managed.
- Track scheduled async firmware work in an internal list protected by a
  spinlock so request_firmware_nowait_cancel() can find and synchronize a
  pending request without weakening the GFP_ATOMIC caller contract.
- Match pending requests by device, callback context and callback function
  instead of matching by callback alone.
- Avoid the devres_add() / schedule_work() ordering race pointed out in
  review.
- Leave devres-managed support for a separate devm_request_firmware_nowait()
  API if needed.
- Link to v2: https://patch.msgid.link/20260430-alsa-hda-tas2781-fw-callback-teardown-v2-0-2c7d89cb3175@gmail.com

Changes in v2:
- Add request_firmware_nowait_cancel() in the firmware loader instead of
  tracking the callback lifetime locally in the TAS2781 HDA driver.
- Keep the TAS2781 change to a cancel/sync call in I2C and SPI unbind.
- Drop the unrelated cached kcontrol pointer cleanup from the previous
  local-driver version.
- Link to v1: https://patch.msgid.link/20260430-alsa-hda-tas2781-fw-callback-teardown-v1-1-874367d6b41b@gmail.com

---
Cássio Gabriel (2):
      firmware_loader: Add cancel helper for async requests
      ALSA: hda/tas2781: Cancel async firmware request at unbind

 drivers/base/firmware_loader/main.c            | 68 ++++++++++++++++++++++++--
 include/linux/firmware.h                       | 10 ++++
 sound/hda/codecs/side-codecs/tas2781_hda_i2c.c |  3 ++
 sound/hda/codecs/side-codecs/tas2781_hda_spi.c |  3 ++
 4 files changed, 80 insertions(+), 4 deletions(-)
---
base-commit: 0d672ef050d4e1c3891c9944f72c85769978bbee
change-id: 20260421-alsa-hda-tas2781-fw-callback-teardown-3b76404bb928

Best regards,
--  
Cássio Gabriel <cassiogabrielcontato@gmail.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v4 1/2] firmware_loader: Add cancel helper for async requests
  2026-05-05 11:18 [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Cássio Gabriel
@ 2026-05-05 11:18 ` Cássio Gabriel
  2026-05-05 11:18 ` [PATCH v4 2/2] ALSA: hda/tas2781: Cancel async firmware request at unbind Cássio Gabriel
  2026-05-06  8:05 ` [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Takashi Iwai
  2 siblings, 0 replies; 6+ messages in thread
From: Cássio Gabriel @ 2026-05-05 11:18 UTC (permalink / raw)
  To: Luis Chamberlain, Russ Weight, Danilo Krummrich,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela
  Cc: driver-core, linux-kernel, linux-sound, Cássio Gabriel

request_firmware_nowait() keeps the callback module pinned and holds
a device reference until the firmware work completes.

Callers still have no way to cancel or synchronize the queued callback
before tearing down their driver-private state.

Track scheduled async firmware work in an internal list and add
request_firmware_nowait_cancel(). The helper cancels work matching the
device, callback context and callback function. It cancels work that has
not started yet and waits for an already-running callback to return. If
the request has already completed, it is a no-op.

Keep the existing request_firmware_nowait() lifetime model manual. A
devres-managed variant can be layered on top separately if needed.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 drivers/base/firmware_loader/main.c | 68 ++++++++++++++++++++++++++++++++++---
 include/linux/firmware.h            | 10 ++++++
 2 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index a11b30dda23b..78c5f05a2ec1 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -1132,6 +1132,7 @@ EXPORT_SYMBOL(release_firmware);
 /* Async support */
 struct firmware_work {
 	struct work_struct work;
+	struct list_head list;
 	struct module *module;
 	const char *name;
 	struct device *device;
@@ -1140,6 +1141,17 @@ struct firmware_work {
 	u32 opt_flags;
 };
 
+static LIST_HEAD(firmware_work_list);
+static DEFINE_SPINLOCK(firmware_work_lock);
+
+static void firmware_work_free(struct firmware_work *fw_work)
+{
+	put_device(fw_work->device); /* taken in request_firmware_nowait() */
+	module_put(fw_work->module);
+	kfree_const(fw_work->name);
+	kfree(fw_work);
+}
+
 static void request_firmware_work_func(struct work_struct *work)
 {
 	struct firmware_work *fw_work;
@@ -1150,11 +1162,15 @@ static void request_firmware_work_func(struct work_struct *work)
 	_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
 			  fw_work->opt_flags);
 	fw_work->cont(fw, fw_work->context);
-	put_device(fw_work->device); /* taken in request_firmware_nowait() */
 
-	module_put(fw_work->module);
-	kfree_const(fw_work->name);
-	kfree(fw_work);
+	spin_lock_irq(&firmware_work_lock);
+	if (!list_empty(&fw_work->list)) {
+		list_del_init(&fw_work->list);
+		spin_unlock_irq(&firmware_work_lock);
+		firmware_work_free(fw_work);
+		return;
+	}
+	spin_unlock_irq(&firmware_work_lock);
 }
 
 
@@ -1164,6 +1180,7 @@ static int _request_firmware_nowait(
 	void (*cont)(const struct firmware *fw, void *context), bool nowarn)
 {
 	struct firmware_work *fw_work;
+	unsigned long flags;
 
 	fw_work = kzalloc_obj(struct firmware_work, gfp);
 	if (!fw_work)
@@ -1196,7 +1213,12 @@ static int _request_firmware_nowait(
 
 	get_device(fw_work->device);
 	INIT_WORK(&fw_work->work, request_firmware_work_func);
+
+	spin_lock_irqsave(&firmware_work_lock, flags);
+	list_add_tail(&fw_work->list, &firmware_work_list);
 	schedule_work(&fw_work->work);
+	spin_unlock_irqrestore(&firmware_work_lock, flags);
+
 	return 0;
 }
 
@@ -1259,6 +1281,44 @@ int firmware_request_nowait_nowarn(
 }
 EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn);
 
+/**
+ * request_firmware_nowait_cancel() - cancel an async firmware request
+ * @device: device for which the firmware is being loaded
+ * @context: context passed to request_firmware_nowait()
+ * @cont: callback passed to request_firmware_nowait()
+ *
+ * Cancel a pending request_firmware_nowait() request for @device, @context
+ * and @cont. If the associated work has already started, this function waits
+ * until the callback has returned. If the callback has already completed, this
+ * function does nothing.
+ *
+ * This function may sleep.
+ */
+void request_firmware_nowait_cancel(struct device *device, void *context,
+				    void (*cont)(const struct firmware *fw,
+						 void *context))
+{
+	struct firmware_work *fw_work = NULL;
+	struct firmware_work *tmp;
+
+	spin_lock_irq(&firmware_work_lock);
+	list_for_each_entry_reverse(tmp, &firmware_work_list, list) {
+		if (tmp->device == device && tmp->context == context &&
+		    tmp->cont == cont) {
+			fw_work = tmp;
+			list_del_init(&fw_work->list);
+			break;
+		}
+	}
+	spin_unlock_irq(&firmware_work_lock);
+
+	if (!fw_work)
+		return;
+	cancel_work_sync(&fw_work->work);
+	firmware_work_free(fw_work);
+}
+EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel);
+
 #ifdef CONFIG_FW_CACHE
 static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
 
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index aae1b85ffc10..0fa3b027f02f 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -110,6 +110,9 @@ int request_firmware_nowait(
 	struct module *module, bool uevent,
 	const char *name, struct device *device, gfp_t gfp, void *context,
 	void (*cont)(const struct firmware *fw, void *context));
+void request_firmware_nowait_cancel(struct device *device, void *context,
+				    void (*cont)(const struct firmware *fw,
+						 void *context));
 int request_firmware_direct(const struct firmware **fw, const char *name,
 			    struct device *device);
 int request_firmware_into_buf(const struct firmware **firmware_p,
@@ -157,6 +160,13 @@ static inline int request_firmware_nowait(
 	return -EINVAL;
 }
 
+static inline void request_firmware_nowait_cancel(struct device *device,
+						  void *context,
+						  void (*cont)(const struct firmware *fw,
+							       void *context))
+{
+}
+
 static inline void release_firmware(const struct firmware *fw)
 {
 }

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v4 2/2] ALSA: hda/tas2781: Cancel async firmware request at unbind
  2026-05-05 11:18 [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Cássio Gabriel
  2026-05-05 11:18 ` [PATCH v4 1/2] firmware_loader: Add cancel helper for async requests Cássio Gabriel
@ 2026-05-05 11:18 ` Cássio Gabriel
  2026-05-06  8:05 ` [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Takashi Iwai
  2 siblings, 0 replies; 6+ messages in thread
From: Cássio Gabriel @ 2026-05-05 11:18 UTC (permalink / raw)
  To: Luis Chamberlain, Russ Weight, Danilo Krummrich,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela
  Cc: driver-core, linux-kernel, linux-sound, Cássio Gabriel,
	stable

TAS2781 HDA I2C and SPI queue RCA firmware loading from component
bind with request_firmware_nowait(). The firmware loader keeps the
callback module pinned and holds a device reference, but the callback
still uses driver-private HDA state.

Component unbind removes controls and DSP state immediately. Later
device removal tears down the TAS2781 private data, including
codec_lock. If the async firmware callback runs after unbind has
started, it can operate on state that is being torn down.

Cancel or synchronize the async firmware request before removing
controls and DSP state. A queued callback is cancelled, and an
already-running callback is allowed to finish before unbind continues.

Fixes: 5be27f1e3ec9 ("ALSA: hda/tas2781: Add tas2781 HDA driver")
Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++
 sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 67240ce184e1..dd1b0cc63ad6 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -588,6 +588,9 @@ static void tas2781_hda_unbind(struct device *dev,
 		comp->playback_hook = NULL;
 	}
 
+	request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv,
+				       tasdev_fw_ready);
+
 	tas2781_hda_remove_controls(tas_hda);
 
 	tasdevice_config_info_remove(tas_hda->priv);
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
index 0e4f3553f273..d243baff95a7 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -750,6 +750,9 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master,
 		comp->playback_hook = NULL;
 	}
 
+	request_firmware_nowait_cancel(tas_priv->dev, tas_priv,
+				       tasdev_fw_ready);
+
 	tas2781_hda_remove_controls(tas_hda);
 
 	tasdevice_config_info_remove(tas_priv);

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown
  2026-05-05 11:18 [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Cássio Gabriel
  2026-05-05 11:18 ` [PATCH v4 1/2] firmware_loader: Add cancel helper for async requests Cássio Gabriel
  2026-05-05 11:18 ` [PATCH v4 2/2] ALSA: hda/tas2781: Cancel async firmware request at unbind Cássio Gabriel
@ 2026-05-06  8:05 ` Takashi Iwai
  2026-05-06  9:34   ` Danilo Krummrich
  2 siblings, 1 reply; 6+ messages in thread
From: Takashi Iwai @ 2026-05-06  8:05 UTC (permalink / raw)
  To: Cássio Gabriel
  Cc: Luis Chamberlain, Russ Weight, Danilo Krummrich,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela, driver-core,
	linux-kernel, linux-sound, stable

On Tue, 05 May 2026 13:18:15 +0200,
Cássio Gabriel wrote:
> 
> TAS2781 HDA I2C and SPI queue RCA firmware loading with
> request_firmware_nowait() during component bind. The firmware loader
> keeps the callback module pinned and holds a device reference, but it
> does not provide a way for drivers to cancel or synchronize the queued
> callback before tearing down driver-private state.
> 
> Add a small firmware-loader helper to cancel or synchronize async firmware
> requests, then use it from TAS2781 HDA unbind before controls and DSP state
> are removed.
> 
> No hardware runtime test was available.
> 
> Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
> ---
> Changes in v4:
> - Use spin_lock_irq() in the worker and cancel paths, which run from
>   sleepable contexts.
> - Fold kfree(fw_work) into firmware_work_free().
> - Keep irqsave in the request path so GFP_ATOMIC callers do not depend on
>   IRQ state assumptions.
> - Link to v3: https://patch.msgid.link/20260501-alsa-hda-tas2781-fw-callback-teardown-v3-0-8d9f873b97bd@gmail.com
> 
> Changes in v3:
> - Keep request_firmware_nowait() manually managed instead of making the
>   existing API implicitly devres-managed.
> - Track scheduled async firmware work in an internal list protected by a
>   spinlock so request_firmware_nowait_cancel() can find and synchronize a
>   pending request without weakening the GFP_ATOMIC caller contract.
> - Match pending requests by device, callback context and callback function
>   instead of matching by callback alone.
> - Avoid the devres_add() / schedule_work() ordering race pointed out in
>   review.
> - Leave devres-managed support for a separate devm_request_firmware_nowait()
>   API if needed.
> - Link to v2: https://patch.msgid.link/20260430-alsa-hda-tas2781-fw-callback-teardown-v2-0-2c7d89cb3175@gmail.com
> 
> Changes in v2:
> - Add request_firmware_nowait_cancel() in the firmware loader instead of
>   tracking the callback lifetime locally in the TAS2781 HDA driver.
> - Keep the TAS2781 change to a cancel/sync call in I2C and SPI unbind.
> - Drop the unrelated cached kcontrol pointer cleanup from the previous
>   local-driver version.
> - Link to v1: https://patch.msgid.link/20260430-alsa-hda-tas2781-fw-callback-teardown-v1-1-874367d6b41b@gmail.com
> 
> ---
> Cássio Gabriel (2):
>       firmware_loader: Add cancel helper for async requests
>       ALSA: hda/tas2781: Cancel async firmware request at unbind

I guess this could go via driver tree?  Or I can take both if I get an
ack, too.

In anyway, for the series:

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown
  2026-05-06  8:05 ` [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Takashi Iwai
@ 2026-05-06  9:34   ` Danilo Krummrich
  2026-05-06 11:14     ` Takashi Iwai
  0 siblings, 1 reply; 6+ messages in thread
From: Danilo Krummrich @ 2026-05-06  9:34 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Cássio Gabriel, Luis Chamberlain, Russ Weight,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela, driver-core,
	linux-kernel, linux-sound, stable

On Wed May 6, 2026 at 10:05 AM CEST, Takashi Iwai wrote:
> On Tue, 05 May 2026 13:18:15 +0200,
> Cássio Gabriel wrote:
>> Cássio Gabriel (2):
>>       firmware_loader: Add cancel helper for async requests
>>       ALSA: hda/tas2781: Cancel async firmware request at unbind

Looks good to me now.

> I guess this could go via driver tree?  Or I can take both if I get an
> ack, too.

Sure, I can pick it up via the driver-core tree, but please also feel free to
take it through the sounds tree.

Acked-by: Danilo Krummrich <dakr@kernel.org>

> In anyway, for the series:
>
> Reviewed-by: Takashi Iwai <tiwai@suse.de>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown
  2026-05-06  9:34   ` Danilo Krummrich
@ 2026-05-06 11:14     ` Takashi Iwai
  0 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2026-05-06 11:14 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Takashi Iwai, Cássio Gabriel, Luis Chamberlain, Russ Weight,
	Greg Kroah-Hartman, Rafael J. Wysocki, Takashi Iwai,
	Shenghao Ding, Kevin Lu, Baojun Xu, Jaroslav Kysela, driver-core,
	linux-kernel, linux-sound, stable

On Wed, 06 May 2026 11:34:39 +0200,
Danilo Krummrich wrote:
> 
> On Wed May 6, 2026 at 10:05 AM CEST, Takashi Iwai wrote:
> > On Tue, 05 May 2026 13:18:15 +0200,
> > Cássio Gabriel wrote:
> >> Cássio Gabriel (2):
> >>       firmware_loader: Add cancel helper for async requests
> >>       ALSA: hda/tas2781: Cancel async firmware request at unbind
> 
> Looks good to me now.
> 
> > I guess this could go via driver tree?  Or I can take both if I get an
> > ack, too.
> 
> Sure, I can pick it up via the driver-core tree, but please also feel free to
> take it through the sounds tree.
> 
> Acked-by: Danilo Krummrich <dakr@kernel.org>

Thanks, then I'm going to take this to sound git tree, as it's the
only user so far.


Takashi

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-05-06 11:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05 11:18 [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Cássio Gabriel
2026-05-05 11:18 ` [PATCH v4 1/2] firmware_loader: Add cancel helper for async requests Cássio Gabriel
2026-05-05 11:18 ` [PATCH v4 2/2] ALSA: hda/tas2781: Cancel async firmware request at unbind Cássio Gabriel
2026-05-06  8:05 ` [PATCH v4 0/2] firmware_loader/ALSA: Fix TAS2781 async firmware teardown Takashi Iwai
2026-05-06  9:34   ` Danilo Krummrich
2026-05-06 11:14     ` Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox