* FAILED: patch "[PATCH] ALSA: core: Check shutdown flag at D0 power-state, too" failed to apply to 5.10-stable tree @ 2026-11-07 11:17 Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin 0 siblings, 1 reply; 6+ messages in thread From: Sasha Levin @ 2026-11-07 11:17 UTC (permalink / raw) To: tiwai; +Cc: stable The patch below does not apply to the 5.10-stable tree. If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <stable@vger.kernel.org>. To reproduce the conflict and resubmit, you may use the following commands: git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y git checkout FETCH_HEAD git cherry-pick -x ad63c5b62843895e8c3cd7291ed07cc659df0355 # <resolve conflicts, build, test, etc.> git commit -s git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261107061731-failed-5.10-ad63c5b62843@kernel.org' --subject-prefix 'PATCH 5.10.y' 'HEAD^..' Thanks, Sasha ------------------ original commit in Linus's tree ------------------ From ad63c5b62843895e8c3cd7291ed07cc659df0355 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <tiwai@suse.de> Date: Tue, 29 Sep 2026 14:53:41 +0200 Subject: [PATCH] ALSA: core: Check shutdown flag at D0 power-state, too The snd_power_ref_and_wait() skips the card->shutdown check when the card is already in D0 state and immediately returns as successful, by assuming the all-green in D0. This assumption makes the code after this sync point behaving as if all power is up and ready, even though actually it might have been already at the disconnected state. Add the missing check of shutdown flag there for the more consistent behavior. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20260929125346.1478631-2-tiwai@suse.de --- sound/core/init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 1bcb6a2e75507..a51b71812fd9b 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1174,12 +1174,12 @@ EXPORT_SYMBOL(snd_card_file_remove); int snd_power_ref_and_wait(struct snd_card *card) { snd_power_ref(card); - if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) - return 0; - wait_event_cmd(card->power_sleep, - card->shutdown || - snd_power_get_state(card) == SNDRV_CTL_POWER_D0, - snd_power_unref(card), snd_power_ref(card)); + if (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + wait_event_cmd(card->power_sleep, + card->shutdown || + snd_power_get_state(card) == SNDRV_CTL_POWER_D0, + snd_power_unref(card), snd_power_ref(card)); + } if (card->shutdown) { snd_power_unref(card); return -ENODEV; -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper 2026-11-07 11:17 FAILED: patch "[PATCH] ALSA: core: Check shutdown flag at D0 power-state, too" failed to apply to 5.10-stable tree Sasha Levin @ 2026-11-10 14:16 ` Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 2/5] ALSA: control: Track in-flight control read/write/tlv accesses Sasha Levin ` (3 more replies) 0 siblings, 4 replies; 6+ messages in thread From: Sasha Levin @ 2026-11-10 14:16 UTC (permalink / raw) To: stable; +Cc: Jaroslav Kysela, Takashi Iwai, Sasha Levin From: Jaroslav Kysela <perex@perex.cz> [ Upstream commit 1fa4445f9adf19a3028ce0e8f375bac75214fc10 ] This helper is required for the following generic LED mute patch. The helper also simplifies some other functions. Signed-off-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20210317172945.842280-2-perex@perex.cz Signed-off-by: Takashi Iwai <tiwai@suse.de> Stable-dep-of: ad63c5b62843 ("ALSA: core: Check shutdown flag at D0 power-state, too") Signed-off-by: Sasha Levin <sashal@kernel.org> --- include/sound/control.h | 4 +-- sound/core/control.c | 68 +++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/include/sound/control.h b/include/sound/control.h index 41bd72ffd2322..46402a3109892 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -115,6 +115,7 @@ typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card, unsigned int cmd, unsigned long arg); void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id); +void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kcontrol * kctl, unsigned int ioff); struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data); void snd_ctl_free_one(struct snd_kcontrol * kcontrol); @@ -123,8 +124,7 @@ int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace); int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id); int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id); -int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, - int active); +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active); struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid); struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id); diff --git a/sound/core/control.c b/sound/core/control.c index 640bafb6605a5..7b76518e862ec 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -182,6 +182,27 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, } EXPORT_SYMBOL(snd_ctl_notify); +/** + * snd_ctl_notify_one - Send notification to user-space for a control change + * @card: the card to send notification + * @mask: the event mask, SNDRV_CTL_EVENT_* + * @kctl: the pointer with the control instance + * @ioff: the additional offset to the control index + * + * This function calls snd_ctl_notify() and does additional jobs + * like LED state changes. + */ +void snd_ctl_notify_one(struct snd_card *card, unsigned int mask, + struct snd_kcontrol *kctl, unsigned int ioff) +{ + struct snd_ctl_elem_id id = kctl->id; + + id.index += ioff; + id.numid += ioff; + snd_ctl_notify(card, mask, &id); +} +EXPORT_SYMBOL(snd_ctl_notify_one); + /** * snd_ctl_new - create a new control instance with some elements * @kctl: the pointer to store new control instance @@ -343,7 +364,6 @@ static int __snd_ctl_add_replace(struct snd_card *card, { struct snd_ctl_elem_id id; unsigned int idx; - unsigned int count; struct snd_kcontrol *old; int err; @@ -377,10 +397,8 @@ static int __snd_ctl_add_replace(struct snd_card *card, kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; - id = kcontrol->id; - count = kcontrol->count; - for (idx = 0; idx < count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); + for (idx = 0; idx < kcontrol->count; idx++) + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx); return 0; } @@ -463,16 +481,14 @@ EXPORT_SYMBOL(snd_ctl_replace); */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { - struct snd_ctl_elem_id id; unsigned int idx; if (snd_BUG_ON(!card || !kcontrol)) return -EINVAL; list_del(&kcontrol->list); card->controls_count -= kcontrol->count; - id = kcontrol->id; - for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id); + for (idx = 0; idx < kcontrol->count; idx++) + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx); snd_ctl_free_one(kcontrol); return 0; } @@ -585,11 +601,13 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } snd_ctl_build_ioff(id, kctl, index_offset); - ret = 1; + downgrade_write(&card->controls_rwsem); + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset); + up_read(&card->controls_rwsem); + return 1; + unlock: up_write(&card->controls_rwsem); - if (ret > 0) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id); return ret; } EXPORT_SYMBOL_GPL(snd_ctl_activate_id); @@ -1111,25 +1129,34 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, unsigned int index_offset; int result; + down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, &control->id); - if (kctl == NULL) + if (kctl == NULL) { + up_write(&card->controls_rwsem); return -ENOENT; + } index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL || (file && vd->owner && vd->owner != file)) { + up_write(&card->controls_rwsem); return -EPERM; } snd_ctl_build_ioff(&control->id, kctl, index_offset); result = kctl->put(kctl, control); - if (result < 0) + if (result < 0) { + up_write(&card->controls_rwsem); return result; + } if (result > 0) { - struct snd_ctl_elem_id id = control->id; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); + downgrade_write(&card->controls_rwsem); + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset); + up_read(&card->controls_rwsem); + } else { + up_write(&card->controls_rwsem); } return 0; @@ -1151,9 +1178,7 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, if (result < 0) goto error; - down_write(&card->controls_rwsem); result = snd_ctl_elem_write(card, file, control); - up_write(&card->controls_rwsem); if (result < 0) goto error; @@ -1302,7 +1327,6 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, { struct user_element *ue = kctl->private_data; unsigned int *container; - struct snd_ctl_elem_id id; unsigned int mask = 0; int i; int change; @@ -1334,10 +1358,8 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, ue->tlv_data_size = size; mask |= SNDRV_CTL_EVENT_MASK_TLV; - for (i = 0; i < kctl->count; ++i) { - snd_ctl_build_ioff(&id, kctl, i); - snd_ctl_notify(ue->card, mask, &id); - } + for (i = 0; i < kctl->count; ++i) + snd_ctl_notify_one(ue->card, mask, kctl, i); return change; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5.10.y 2/5] ALSA: control: Track in-flight control read/write/tlv accesses 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin @ 2026-11-10 14:16 ` Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 3/5] ALSA: core: Simplify snd_power_ref_and_wait() with the standard macro Sasha Levin ` (2 subsequent siblings) 3 siblings, 0 replies; 6+ messages in thread From: Sasha Levin @ 2026-11-10 14:16 UTC (permalink / raw) To: stable; +Cc: Takashi Iwai, Jaroslav Kysela, Sasha Levin From: Takashi Iwai <tiwai@suse.de> [ Upstream commit e94fdbd7b25d87e64688bb109e2c550217a4c879 ] Although the power state check is performed in various places (e.g. at the entrance of quite a few ioctls), there can be still some pending tasks that already went into the ioctl handler or other ops, and those may access the hardware even after the power state check. For example, kcontrol access ioctl paths that call info/get/put callbacks may update the hardware registers. If a system wants to assure the free from such hw access (like the case of PCI rescan feature we're going to implement in future), this situation must be avoided, and we have to sync such in-flight tasks finishing beforehand. For that purpose, this patch introduces a few new things in core code: - A refcount, power_ref, and a wait queue, power_ref_sleep, to the card object - A few new helpers, snd_power_ref(), snd_power_unref(), snd_power_ref_and_wait(), and snd_power_sync_ref() In the code paths that call kctl info/read/write/tlv ops, we check the power state with the newly introduced snd_power_ref_and_wait(). This function also takes the card.power_ref refcount for tracking this in-flight task. Once after the access finishes, snd_power_unref() is called to released the refcount in return. So the driver can sync via snd_power_sync_ref() assuring that all in-flight tasks have been finished. As of this patch, snd_power_sync_ref() is called only at snd_card_disconnect(), but it'll be used in other places in future. Note that atomic_t is used for power_ref intentionally instead of refcount_t. It's because of the design of refcount_t type; refcount_t cannot be zero-based, and it cannot do dec_and_test() call for multiple times, hence it's not suitable for our purpose. Also, this patch changes snd_power_wait() to accept only SNDRV_CTL_POWER_D0, which is the only value that makes sense. In later patch, the snd_power_wait() calls will be cleaned up. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20210523090920.15345-3-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de> Stable-dep-of: ad63c5b62843 ("ALSA: core: Check shutdown flag at D0 power-state, too") Signed-off-by: Sasha Levin <sashal@kernel.org> --- include/sound/core.h | 42 +++++++++++++++++++++++++++++++ sound/core/control.c | 23 +++++++++++++---- sound/core/control_compat.c | 5 +++- sound/core/init.c | 50 +++++++++++++++++++++++++++++++------ 4 files changed, 106 insertions(+), 14 deletions(-) diff --git a/include/sound/core.h b/include/sound/core.h index 85610ede9ea01..ac915ce0f3aa1 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -125,7 +125,9 @@ struct snd_card { #ifdef CONFIG_PM unsigned int power_state; /* power state */ + atomic_t power_ref; wait_queue_head_t power_sleep; + wait_queue_head_t power_ref_sleep; #endif #if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -148,12 +150,52 @@ static inline void snd_power_change_state(struct snd_card *card, unsigned int st wake_up(&card->power_sleep); } +/** + * snd_power_ref - Take the reference count for power control + * @card: sound card object + * + * The power_ref reference of the card is used for managing to block + * the snd_power_sync_ref() operation. This function increments the reference. + * The counterpart snd_power_unref() has to be called appropriately later. + */ +static inline void snd_power_ref(struct snd_card *card) +{ + atomic_inc(&card->power_ref); +} + +/** + * snd_power_unref - Release the reference count for power control + * @card: sound card object + */ +static inline void snd_power_unref(struct snd_card *card) +{ + if (atomic_dec_and_test(&card->power_ref)) + wake_up(&card->power_ref_sleep); +} + +/** + * snd_power_sync_ref - wait until the card power_ref is freed + * @card: sound card object + * + * This function is used to synchronize with the pending power_ref being + * released. + */ +static inline void snd_power_sync_ref(struct snd_card *card) +{ + wait_event(card->power_ref_sleep, !atomic_read(&card->power_ref)); +} + /* init.c */ int snd_power_wait(struct snd_card *card, unsigned int power_state); +int snd_power_ref_and_wait(struct snd_card *card); #else /* ! CONFIG_PM */ static inline int snd_power_wait(struct snd_card *card, unsigned int state) { return 0; } +static inline void snd_power_ref(struct snd_card *card) {} +static inline void snd_power_unref(struct snd_card *card) {} +static inline int snd_power_ref_and_wait(struct snd_card *card) { return 0; } +static inline void snd_power_sync_ref(struct snd_card *card) {} #define snd_power_get_state(card) ({ (void)(card); SNDRV_CTL_POWER_D0; }) #define snd_power_change_state(card, state) do { (void)(card); } while (0) diff --git a/sound/core/control.c b/sound/core/control.c index 7b76518e862ec..e5b8c4393c1a9 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -987,7 +987,10 @@ static int __snd_ctl_elem_info(struct snd_card *card, #ifdef CONFIG_SND_DEBUG info->access = 0; #endif - result = kctl->info(kctl, info); + result = snd_power_ref_and_wait(card); + if (!result) + result = kctl->info(kctl, info); + snd_power_unref(card); if (result >= 0) { snd_BUG_ON(info->access); index_offset = snd_ctl_get_ioff(kctl, &info->id); @@ -1079,7 +1082,10 @@ static int snd_ctl_elem_read(struct snd_card *card, if (!snd_ctl_skip_validation(&info)) fill_remaining_elem_value(control, &info, pattern); - ret = kctl->get(kctl, control); + ret = snd_power_ref_and_wait(card); + if (!ret) + ret = kctl->get(kctl, control); + snd_power_unref(card); if (ret < 0) return ret; if (!snd_ctl_skip_validation(&info) && @@ -1145,7 +1151,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, } snd_ctl_build_ioff(&control->id, kctl, index_offset); - result = kctl->put(kctl, control); + result = snd_power_ref_and_wait(card); + if (!result) + result = kctl->put(kctl, control); + snd_power_unref(card); if (result < 0) { up_write(&card->controls_rwsem); return result; @@ -1633,7 +1642,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND}, }; struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)]; - int i; + int i, ret; /* Check support of the request for this element. */ for (i = 0; i < ARRAY_SIZE(pairs); ++i) { @@ -1651,7 +1660,11 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, vd->owner != NULL && vd->owner != file) return -EPERM; - return kctl->tlv.c(kctl, op_flag, size, buf); + ret = snd_power_ref_and_wait(file->card); + if (!ret) + ret = kctl->tlv.c(kctl, op_flag, size, buf); + snd_power_unref(file->card); + return ret; } static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id, diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 980ab3580f1b7..3c58741c674f7 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -187,7 +187,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, return -ENOMEM; } info->id = *id; - err = kctl->info(kctl, info); + err = snd_power_ref_and_wait(card); + if (!err) + err = kctl->info(kctl, info); + snd_power_unref(card); up_read(&card->controls_rwsem); if (err >= 0) { err = info->type; diff --git a/sound/core/init.c b/sound/core/init.c index 476ab6df3aeb9..f5b97ebba1c18 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -218,6 +218,8 @@ int snd_card_new(struct device *parent, int idx, const char *xid, mutex_init(&card->memory_mutex); #ifdef CONFIG_PM init_waitqueue_head(&card->power_sleep); + init_waitqueue_head(&card->power_ref_sleep); + atomic_set(&card->power_ref, 0); #endif init_waitqueue_head(&card->remove_sleep); card->sync_irq = -1; @@ -434,6 +436,7 @@ int snd_card_disconnect(struct snd_card *card) #ifdef CONFIG_PM wake_up(&card->power_sleep); + snd_power_sync_ref(card); #endif return 0; } @@ -995,21 +998,28 @@ EXPORT_SYMBOL(snd_card_file_remove); #ifdef CONFIG_PM /** - * snd_power_wait - wait until the power-state is changed. - * @card: soundcard structure - * @power_state: expected power state + * snd_power_ref_and_wait - wait until the card gets powered up + * @card: soundcard structure * - * Waits until the power-state is changed. + * Take the power_ref reference count of the given card, and + * wait until the card gets powered up to SNDRV_CTL_POWER_D0 state. + * The refcount is down again while sleeping until power-up, hence this + * function can be used for syncing the floating control ops accesses, + * typically around calling control ops. * - * Return: Zero if successful, or a negative error code. + * The caller needs to pull down the refcount via snd_power_unref() later + * no matter whether the error is returned from this function or not. + * + * Return: Zero if successful, or a negative error code. */ -int snd_power_wait(struct snd_card *card, unsigned int power_state) +int snd_power_ref_and_wait(struct snd_card *card) { wait_queue_entry_t wait; int result = 0; + snd_power_ref(card); /* fastpath */ - if (snd_power_get_state(card) == power_state) + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) return 0; init_waitqueue_entry(&wait, current); add_wait_queue(&card->power_sleep, &wait); @@ -1018,13 +1028,37 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) result = -ENODEV; break; } - if (snd_power_get_state(card) == power_state) + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) break; + snd_power_unref(card); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(30 * HZ); + snd_power_ref(card); } remove_wait_queue(&card->power_sleep, &wait); return result; } +EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); + +/** + * snd_power_wait - wait until the card gets powered up (old form) + * @card: soundcard structure + * @power_state: expected power state + * + * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state. + * @power_state must be SNDRV_CTL_POWER_D0. + * + * Return: Zero if successful, or a negative error code. + */ +int snd_power_wait(struct snd_card *card, unsigned int power_state) +{ + int ret; + + if (WARN_ON(power_state != SNDRV_CTL_POWER_D0)) + return 0; + ret = snd_power_ref_and_wait(card); + snd_power_unref(card); + return ret; +} EXPORT_SYMBOL(snd_power_wait); #endif /* CONFIG_PM */ -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5.10.y 3/5] ALSA: core: Simplify snd_power_ref_and_wait() with the standard macro 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 2/5] ALSA: control: Track in-flight control read/write/tlv accesses Sasha Levin @ 2026-11-10 14:16 ` Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 4/5] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 5/5] ALSA: core: Check shutdown flag at D0 power-state, too Sasha Levin 3 siblings, 0 replies; 6+ messages in thread From: Sasha Levin @ 2026-11-10 14:16 UTC (permalink / raw) To: stable; +Cc: Takashi Iwai, Jaroslav Kysela, Sasha Levin From: Takashi Iwai <tiwai@suse.de> [ Upstream commit 8c0ae778e2874f3742bd619000b791d178c187e2 ] Use wait_event_cmd() macro and simplify snd_power_ref_wait() implementation. This may also cover possible races in the current open code, too. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20220119091050.30125-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de> Stable-dep-of: ad63c5b62843 ("ALSA: core: Check shutdown flag at D0 power-state, too") Signed-off-by: Sasha Levin <sashal@kernel.org> --- sound/core/init.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index f5b97ebba1c18..279c630438882 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1014,29 +1014,14 @@ EXPORT_SYMBOL(snd_card_file_remove); */ int snd_power_ref_and_wait(struct snd_card *card) { - wait_queue_entry_t wait; - int result = 0; - snd_power_ref(card); - /* fastpath */ if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) return 0; - init_waitqueue_entry(&wait, current); - add_wait_queue(&card->power_sleep, &wait); - while (1) { - if (card->shutdown) { - result = -ENODEV; - break; - } - if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) - break; - snd_power_unref(card); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(30 * HZ); - snd_power_ref(card); - } - remove_wait_queue(&card->power_sleep, &wait); - return result; + wait_event_cmd(card->power_sleep, + card->shutdown || + snd_power_get_state(card) == SNDRV_CTL_POWER_D0, + snd_power_unref(card), snd_power_ref(card)); + return card->shutdown ? -ENODEV : 0; } EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5.10.y 4/5] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 2/5] ALSA: control: Track in-flight control read/write/tlv accesses Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 3/5] ALSA: core: Simplify snd_power_ref_and_wait() with the standard macro Sasha Levin @ 2026-11-10 14:16 ` Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 5/5] ALSA: core: Check shutdown flag at D0 power-state, too Sasha Levin 3 siblings, 0 replies; 6+ messages in thread From: Sasha Levin @ 2026-11-10 14:16 UTC (permalink / raw) To: stable; +Cc: Takashi Iwai, WenTao Liang, Sasha Levin From: Takashi Iwai <tiwai@suse.de> [ Upstream commit b113a891252c3fa4fab11ec8c2894a22ecaf278c ] snd_power_ref_and_wait() takes the power refcount and doesn't leave it no matter whether it returns an error or not. However, the majority of callers don't expect but just returns without unreferencing in the caller side upon errors. For addressing the potential refcount unbalance, rather correct the behavior of snd_power_ref_wait() to unreference upon returning an error. Note that the problem above is likely negligible; the function returns an error only when the sound card is being shutdown, hence it doesn't matter about the power refcount any longer at such a state. Fixes: e94fdbd7b25d ("ALSA: control: Track in-flight control read/write/tlv accesses") Reported-by: WenTao Liang <vulab@iscas.ac.cn> Closes: https://lore.kernel.org/20260612022121.14329-1-vulab@iscas.ac.cn Link: https://patch.msgid.link/20260614090507.772540-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de> Stable-dep-of: ad63c5b62843 ("ALSA: core: Check shutdown flag at D0 power-state, too") Signed-off-by: Sasha Levin <sashal@kernel.org> --- sound/core/init.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 279c630438882..1759ef9ae8b4e 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1008,7 +1008,7 @@ EXPORT_SYMBOL(snd_card_file_remove); * typically around calling control ops. * * The caller needs to pull down the refcount via snd_power_unref() later - * no matter whether the error is returned from this function or not. + * when this function returns 0. * * Return: Zero if successful, or a negative error code. */ @@ -1021,7 +1021,11 @@ int snd_power_ref_and_wait(struct snd_card *card) card->shutdown || snd_power_get_state(card) == SNDRV_CTL_POWER_D0, snd_power_unref(card), snd_power_ref(card)); - return card->shutdown ? -ENODEV : 0; + if (card->shutdown) { + snd_power_unref(card); + return -ENODEV; + } + return 0; } EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); @@ -1042,7 +1046,8 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) if (WARN_ON(power_state != SNDRV_CTL_POWER_D0)) return 0; ret = snd_power_ref_and_wait(card); - snd_power_unref(card); + if (!ret) + snd_power_unref(card); return ret; } EXPORT_SYMBOL(snd_power_wait); -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5.10.y 5/5] ALSA: core: Check shutdown flag at D0 power-state, too 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin ` (2 preceding siblings ...) 2026-11-10 14:16 ` [PATCH 5.10.y 4/5] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() Sasha Levin @ 2026-11-10 14:16 ` Sasha Levin 3 siblings, 0 replies; 6+ messages in thread From: Sasha Levin @ 2026-11-10 14:16 UTC (permalink / raw) To: stable; +Cc: Takashi Iwai, Sasha Levin From: Takashi Iwai <tiwai@suse.de> [ Upstream commit ad63c5b62843895e8c3cd7291ed07cc659df0355 ] The snd_power_ref_and_wait() skips the card->shutdown check when the card is already in D0 state and immediately returns as successful, by assuming the all-green in D0. This assumption makes the code after this sync point behaving as if all power is up and ready, even though actually it might have been already at the disconnected state. Add the missing check of shutdown flag there for the more consistent behavior. Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20260929125346.1478631-2-tiwai@suse.de Signed-off-by: Sasha Levin <sashal@kernel.org> --- sound/core/init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 1759ef9ae8b4e..e55f95eb2362f 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1015,12 +1015,12 @@ EXPORT_SYMBOL(snd_card_file_remove); int snd_power_ref_and_wait(struct snd_card *card) { snd_power_ref(card); - if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) - return 0; - wait_event_cmd(card->power_sleep, - card->shutdown || - snd_power_get_state(card) == SNDRV_CTL_POWER_D0, - snd_power_unref(card), snd_power_ref(card)); + if (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + wait_event_cmd(card->power_sleep, + card->shutdown || + snd_power_get_state(card) == SNDRV_CTL_POWER_D0, + snd_power_unref(card), snd_power_ref(card)); + } if (card->shutdown) { snd_power_unref(card); return -ENODEV; -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-10 14:16 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-07 11:17 FAILED: patch "[PATCH] ALSA: core: Check shutdown flag at D0 power-state, too" failed to apply to 5.10-stable tree Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 1/5] ALSA: control - introduce snd_ctl_notify_one() helper Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 2/5] ALSA: control: Track in-flight control read/write/tlv accesses Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 3/5] ALSA: core: Simplify snd_power_ref_and_wait() with the standard macro Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 4/5] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() Sasha Levin 2026-11-10 14:16 ` [PATCH 5.10.y 5/5] ALSA: core: Check shutdown flag at D0 power-state, too Sasha Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox