FAILED: patch "[PATCH] ALSA: usb-audio: Complete cleanup after system-resume errors" failed to apply to 5.10-stable tree
Subsystems (2)
[email protected]
maintainerThe 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 <[email protected]>.
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 1739a976312e110c93a8dee66a1cdf893a1b187e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083119-straddle-squeegee-5817@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 1739a976312e110c93a8dee66a1cdf893a1b187e Mon Sep 17 00:00:00 2001
From: Will Porter <[email protected]>
Date: Mon, 24 Aug 2026 17:57:57 -0500
Subject: [PATCH] ALSA: usb-audio: Complete cleanup after system-resume errors
A failed system resume can leave the card unusable until reboot. usb_audio_resume() jumps to err_out when snd_usb_pcm_resume() or snd_usb_mixer_resume() fails. The error path skips the out: block, which restores D0 and decrements chip->num_suspended_intf.
The card stays in SNDRV_CTL_POWER_D3hot, so later control access blocks in snd_power_ref_and_wait(). USB core logs an interface resume callback error. It does not retry that callback, so a later callback cannot complete the skipped cleanup.
usb_audio_suspend() increments num_suspended_intf before returning success. A system-resume callback must consume the system-suspend count even if a component resume fails. Otherwise, the stranded count skews later suspend and resume cycles.
Do not apply this cleanup to runtime-resume errors. Runtime PM can retry -EAGAIN or -EBUSY without another suspend callback. The count must continue to describe that suspended interface. Other runtime-resume errors latch runtime_error in the PM core and do not cause an immediate callback retry.
Both parts of the system-resume error path are longstanding. Commit 88a8516a2128a ("ALSA: usbaudio: implement USB autosuspend") introduced err_out past the D0 restore. Commit 862b2509d157c ("ALSA: usb-audio: Fix inconsistent card PM state after resume") later moved num_suspended_intf-- into the out: block. The error path now skips both operations.
No third-party code is needed to reach the error path. snd_usb_mixer_resume() ends in snd_usb_mixer_activate(), which returns the result of usb_submit_urb() for devices that have a mixer status URB. Its mixer->private_resume hook can also fail through scarlett2_init_notify(). snd_usb_pcm_resume() issues a SET_CUR request to a UAC3 power domain. It can return -EPIPE or -EIO when the device stalls the request.
Route a component error through out: only when system_suspend is nonzero. Continue to return runtime-resume errors through err_out. Later component resume stages remain skipped. The original error still reaches USB core. A later transfer can fail if the device did not recover.
I reproduced the system-resume failure on an Audient iD14 MkI with an out-of-tree diagnostic mixer resume hook. An injected -EIO on the unpatched core left control readers in uninterruptible sleep in snd_power_ref_and_wait() until a reboot. With this patch, the same failure restored control access. A second system suspend and resume also succeeded after I disabled fault injection.
sound/usb/card.c +10 -4
@@ -1282,8 +1282,11 @@ static int usb_audio_resume(struct usb_interface *intf)list_for_each_entry (as, &chip->pcm_list, list) { err =snd_usb_pcm_resume (as); -if (err <0 ) - goto err_out; +if (err <0 ) { +if (!chip->system_suspend) + goto err_out; + goto out; + } }/*
@@ -1292,8 +1295,11 @@ static int usb_audio_resume(struct usb_interface *intf)*/ list_for_each_entry (mixer, &chip->mixer_list, list) { err =snd_usb_mixer_resume (mixer); -if (err <0 ) - goto err_out; +if (err <0 ) { +if (!chip->system_suspend) + goto err_out; + goto out; + } }list_for_each (p, &chip->midi_list) {
Sasha Levin
maintainerPATCH 1/5 ALSA: usb-audio: Carve out connector value checking into a helper
From: Kai-Heng Feng <[email protected]>
[ Upstream commit 69644fca2716699cc6896aff51d37b1e886c94d2 ]
This is preparation for next patch, no functional change intended.
Stable-dep-of: 1739a976312e ("ALSA: usb-audio: Complete cleanup after system-resume errors")
--- sound/usb/mixer.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-)
sound/usb/mixer.c +25 -9
@@ -1454,13 +1454,11 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,return 0 ; } -/* get the connectors status and report it as boolean type */ -static int mixer_ctl_connector_get (struct snd_kcontrol *kcontrol, -struct snd_ctl_elem_value *ucontrol) +static int get_connector_value (struct usb_mixer_elem_info *cval, +char *name,int *val) { -struct usb_mixer_elem_info *cval = kcontrol->private_data;struct snd_usb_audio *chip = cval->head.mixer->chip; -int idx =0 , validx, ret, val; +int idx =0 , validx, ret; validx = cval->control <<8 |0 ;
@@ -1475,21 +1473,24 @@ static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, ret =snd_usb_ctl_msg (chip->dev,usb_rcvctrlpipe (chip->dev,0 ),UAC2_CS_CUR ,USB_RECIP_INTERFACE |USB_TYPE_CLASS |USB_DIR_IN , validx, idx, &uac2_conn,sizeof (uac2_conn)); - val = !!uac2_conn.bNrChannels; +if (val) + *val = !!uac2_conn.bNrChannels; }else {/* UAC_VERSION_3 */ struct uac3_insertion_ctl_blk uac3_conn; ret =snd_usb_ctl_msg (chip->dev,usb_rcvctrlpipe (chip->dev,0 ),UAC2_CS_CUR ,USB_RECIP_INTERFACE |USB_TYPE_CLASS |USB_DIR_IN , validx, idx, &uac3_conn,sizeof (uac3_conn)); - val = !!uac3_conn.bmConInserted; +if (val) + *val = !!uac3_conn.bmConInserted; }snd_usb_unlock_shutdown (chip);if (ret <0 ) { -if (strstr (kcontrol->id.name,"Speaker" )) { - ucontrol->value.integer.value[0 ] =1 ; +if (name &&strstr (name,"Speaker" )) { +if (val) + *val =1 ;return 0 ; } error:
@@ -1499,6 +1500,21 @@ static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,return filter_error (cval, ret); } +return ret; +} + +/* get the connectors status and report it as boolean type */ +static int mixer_ctl_connector_get (struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ +struct usb_mixer_elem_info *cval = kcontrol->private_data; +int ret, val; + + ret =get_connector_value (cval, kcontrol->id.name, &val); + +if (ret <0 ) +return ret; + ucontrol->value.integer.value[0 ] = val;return 0 ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 ALSA: usb-audio: Check connector value on resume
From: Kai-Heng Feng <[email protected]>
[ Upstream commit 44609fc01f280d6b4067262ecbb00e3128d718ae ]
Rear Mic on Lenovo P620 cannot record after S3, despite that there's no error and the other two functions of the USB audio, Line In and Line Out, work just fine.
The mic starts to work again after running userspace app like "alsactl store". Following the lead, the evidence shows that as soon as connector status is queried, the mic can work again.
So also check connector value on resume to "wake up" the USB audio to make it functional.
This can be device specific, however I think this generic approach may benefit more than one device.
Now the resume callback checks connector, and a new callback, reset_resume, to also restore switches and volumes.
Stable-dep-of: 1739a976312e ("ALSA: usb-audio: Complete cleanup after system-resume errors")
--- sound/usb/mixer.c | 44 +++++++++++++++++++++++++++++++--------- sound/usb/mixer.h | 1 + sound/usb/mixer_quirks.c | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-)
sound/usb/mixer.c +34 -10
@@ -3719,20 +3719,43 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)return 0 ; } +static int default_mixer_resume (struct usb_mixer_elem_list *list) +{ +struct usb_mixer_elem_info *cval =mixer_elem_list_to_info (list); + +/* get connector value to "wake up" the USB audio */ +if (cval->val_type ==USB_MIXER_BOOLEAN && cval->channels ==1 ) +get_connector_value (cval,NULL ,NULL ); + +return 0 ; +} + +static int default_mixer_reset_resume (struct usb_mixer_elem_list *list) +{ +int err =default_mixer_resume (list); + +if (err <0 ) +return err; +return restore_mixer_value (list); +} +int snd_usb_mixer_resume (struct usb_mixer_interface *mixer,bool reset_resume) {struct usb_mixer_elem_list *list; +usb_mixer_elem_resume_func_t f;int id, err; -if (reset_resume) { -/* restore cached mixer values */ -for (id =0 ; id <MAX_ID_ELEMS ; id++) { -for_each_mixer_elem (list, mixer, id) { -if (list->resume) { - err = list->resume (list); -if (err <0 ) -return err; - } +/* restore cached mixer values */ +for (id =0 ; id <MAX_ID_ELEMS ; id++) { +for_each_mixer_elem (list, mixer, id) { +if (reset_resume) + f = list->reset_resume; +else + f = list->resume; +if (f) { + err =f (list); +if (err <0 ) +return err; } } }
@@ -3751,6 +3774,7 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->id = unitid; list->dump = snd_usb_mixer_dump_cval;#ifdef CONFIG_PM - list->resume = restore_mixer_value; + list->resume = default_mixer_resume; + list->reset_resume = default_mixer_reset_resume;#endif }
sound/usb/mixer.h +1 -0
@@ -70,6 +70,7 @@ struct usb_mixer_elem_list {bool is_std_info;usb_mixer_elem_dump_func_t dump;usb_mixer_elem_resume_func_t resume; +usb_mixer_elem_resume_func_t reset_resume; };/* iterate over mixer element list of the given unit id */
sound/usb/mixer_quirks.c +1 -1
@@ -154,7 +154,7 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, *listp = list; list->mixer = mixer; list->id = id; - list->resume = resume; + list->reset_resume = resume; kctl =snd_ctl_new1 (knew, list);if (!kctl) {kfree (list);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 ALSA: usb-audio: Avoid unnecessary or invalid connector selection at resume
In reply to Sasha LevinFrom: Takashi Iwai <[email protected]>
[ Upstream commit 8dde723fcde4479f256441da03793e37181d9f21 ]
The recent fix for the resume on Lenovo machines seems causing a regression on others. It's because the change always triggers the connector selection no matter which widget node type is.
This patch addresses the regression by setting the resume callback selectively only for the connector widget.
Stable-dep-of: 1739a976312e ("ALSA: usb-audio: Complete cleanup after system-resume errors")
--- sound/usb/mixer.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-)
sound/usb/mixer.c +20 -15
@@ -1824,6 +1824,15 @@ static void get_connector_control_name(struct usb_mixer_interface *mixer,strlcat (name," - Output Jack" , name_size); } +/* get connector value to "wake up" the USB audio */ +static int connector_mixer_resume (struct usb_mixer_elem_list *list) +{ +struct usb_mixer_elem_info *cval =mixer_elem_list_to_info (list); + +get_connector_value (cval,NULL ,NULL ); +return 0 ; +} +/* Build a mixer control for a UAC connector control (jack-detect) */ static void build_connector_control(struct usb_mixer_interface *mixer, const struct usbmix_name_map *imap,
@@ -1841,6 +1850,10 @@ static void build_connector_control(struct usb_mixer_interface *mixer,if (!cval)return ;snd_usb_mixer_elem_init_std (&cval->head, mixer, term->id); + +/* set up a specific resume callback */ + cval->head.resume = connector_mixer_resume; +/* * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the * number of channels connected.
@@ -3719,23 +3732,15 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)return 0 ; } -static int default_mixer_resume (struct usb_mixer_elem_list *list) -{ -struct usb_mixer_elem_info *cval =mixer_elem_list_to_info (list); - -/* get connector value to "wake up" the USB audio */ -if (cval->val_type ==USB_MIXER_BOOLEAN && cval->channels ==1 ) -get_connector_value (cval,NULL ,NULL ); - -return 0 ; -} -static int default_mixer_reset_resume (struct usb_mixer_elem_list *list) { -int err =default_mixer_resume (list); +int err; -if (err <0 ) -return err; +if (list->resume) { + err = list->resume (list); +if (err <0 ) +return err; + }return restore_mixer_value (list); }
@@ -3774,7 +3779,7 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->id = unitid; list->dump = snd_usb_mixer_dump_cval;#ifdef CONFIG_PM - list->resume = default_mixer_resume; + list->resume =NULL ; list->reset_resume = default_mixer_reset_resume;#endif }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/5 ALSA: usb-audio: Unify mixer resume and reset_resume procedure
In reply to Sasha LevinFrom: Takashi Iwai <[email protected]>
[ Upstream commit 7b9cf9036609428e845dc300aec13822ba2c4ab3 ]
USB-audio driver assumes that the normal resume would preserve the device configuration while reset_resume wouldn't, and tries to restore the mixer elements only at reset_resume callback. However, this seems too naive, and some devices do behave differently, resetting the volume at the normal resume; this resulted in the inconsistent volume that surprised users.
This patch changes the mixer resume code to handle both the normal and reset resume in the same way, always restoring the original mixer element values. This allows us to unify the both callbacks as well as dropping the no longer used reset_resume field, which ends up with a good code reduction.
A slight behavior change by this patch is that now we assign restore_mixer_value() as the default resume callback, and the function is no longer called at reset-resume when the resume callback is overridden by the quirk function. That is, if needed, the quirk resume function would have to handle similarly as restore_mixer_value() by itself.
Stable-dep-of: 1739a976312e ("ALSA: usb-audio: Complete cleanup after system-resume errors")
--- sound/usb/card.c | 18 ++++-------------- sound/usb/mixer.c | 26 ++++---------------------- sound/usb/mixer.h | 3 +-- sound/usb/mixer_quirks.c | 2 +- 4 files changed, 10 insertions(+), 39 deletions(-)
sound/usb/card.c +4 -14
@@ -1030,7 +1030,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)return 0 ; } -static int __usb_audio_resume (struct usb_interface *intf,bool reset_resume) +static int usb_audio_resume (struct usb_interface *intf) {struct snd_usb_audio *chip =usb_get_intfdata (intf);struct snd_usb_stream *as;
@@ -1056,7 +1056,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)* we just notify and restart the mixers */ list_for_each_entry (mixer, &chip->mixer_list, list) { - err =snd_usb_mixer_resume (mixer, reset_resume); + err =snd_usb_mixer_resume (mixer);if (err <0 ) goto err_out; }
@@ -1076,20 +1076,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)atomic_dec (&chip->active);/* allow autopm after this point */ return err; } - -static int usb_audio_resume (struct usb_interface *intf) -{ -return __usb_audio_resume (intf, false); -} - -static int usb_audio_reset_resume (struct usb_interface *intf) -{ -return __usb_audio_resume (intf, true); -}#else #define usb_audio_suspend NULL#define usb_audio_resume NULL -#define usb_audio_reset_resume NULL +#define usb_audio_resume NULL#endif /* CONFIG_PM */ static const struct usb_device_id usb_audio_ids [] = {
@@ -1111,7 +1101,7 @@ static struct usb_driver usb_audio_driver = { .disconnect = usb_audio_disconnect, .suspend = usb_audio_suspend, .resume = usb_audio_resume, - .reset_resume = usb_audio_reset_resume, + .reset_resume = usb_audio_resume, .id_table = usb_audio_ids, .supports_autosuspend = 1, };
sound/usb/mixer.c +4 -22
@@ -3732,33 +3732,16 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)return 0 ; } -static int default_mixer_reset_resume (struct usb_mixer_elem_list *list) -{ -int err; - -if (list->resume) { - err = list->resume (list); -if (err <0 ) -return err; - } -return restore_mixer_value (list); -} - -int snd_usb_mixer_resume (struct usb_mixer_interface *mixer,bool reset_resume) +int snd_usb_mixer_resume (struct usb_mixer_interface *mixer) {struct usb_mixer_elem_list *list; -usb_mixer_elem_resume_func_t f;int id, err;/* restore cached mixer values */ for (id =0 ; id <MAX_ID_ELEMS ; id++) {for_each_mixer_elem (list, mixer, id) { -if (reset_resume) - f = list->reset_resume; -else - f = list->resume; -if (f) { - err =f (list); +if (list->resume) { + err = list->resume (list);if (err <0 )return err; }
@@ -3779,7 +3762,6 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->id = unitid; list->dump = snd_usb_mixer_dump_cval;#ifdef CONFIG_PM - list->resume =NULL ; - list->reset_resume = default_mixer_reset_resume; + list->resume = restore_mixer_value;#endif }
sound/usb/mixer.h +1 -2
@@ -70,7 +70,6 @@ struct usb_mixer_elem_list {bool is_std_info;usb_mixer_elem_dump_func_t dump;usb_mixer_elem_resume_func_t resume; -usb_mixer_elem_resume_func_t reset_resume; };/* iterate over mixer element list of the given unit id */
@@ -122,7 +121,7 @@ int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,#ifdef CONFIG_PM int snd_usb_mixer_suspend (struct usb_mixer_interface *mixer); -int snd_usb_mixer_resume (struct usb_mixer_interface *mixer,bool reset_resume); +int snd_usb_mixer_resume (struct usb_mixer_interface *mixer);#endif int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
sound/usb/mixer_quirks.c +1 -1
@@ -154,7 +154,7 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, *listp = list; list->mixer = mixer; list->id = id; - list->reset_resume = resume; + list->resume = resume; kctl =snd_ctl_new1 (knew, list);if (!kctl) {kfree (list);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 ALSA: usb-audio: Complete cleanup after system-resume errors
In reply to Sasha LevinFrom: Will Porter <[email protected]>
[ Upstream commit 1739a976312e110c93a8dee66a1cdf893a1b187e ]
A failed system resume can leave the card unusable until reboot. usb_audio_resume() jumps to err_out when snd_usb_pcm_resume() or snd_usb_mixer_resume() fails. The error path skips the out: block, which restores D0 and decrements chip->num_suspended_intf.
The card stays in SNDRV_CTL_POWER_D3hot, so later control access blocks in snd_power_ref_and_wait(). USB core logs an interface resume callback error. It does not retry that callback, so a later callback cannot complete the skipped cleanup.
usb_audio_suspend() increments num_suspended_intf before returning success. A system-resume callback must consume the system-suspend count even if a component resume fails. Otherwise, the stranded count skews later suspend and resume cycles.
Do not apply this cleanup to runtime-resume errors. Runtime PM can retry -EAGAIN or -EBUSY without another suspend callback. The count must continue to describe that suspended interface. Other runtime-resume errors latch runtime_error in the PM core and do not cause an immediate callback retry.
Both parts of the system-resume error path are longstanding. Commit 88a8516a2128a ("ALSA: usbaudio: implement USB autosuspend") introduced err_out past the D0 restore. Commit 862b2509d157c ("ALSA: usb-audio: Fix inconsistent card PM state after resume") later moved num_suspended_intf-- into the out: block. The error path now skips both operations.
No third-party code is needed to reach the error path. snd_usb_mixer_resume() ends in snd_usb_mixer_activate(), which returns the result of usb_submit_urb() for devices that have a mixer status URB. Its mixer->private_resume hook can also fail through scarlett2_init_notify(). snd_usb_pcm_resume() issues a SET_CUR request to a UAC3 power domain. It can return -EPIPE or -EIO when the device stalls the request.
Route a component error through out: only when system_suspend is nonzero. Continue to return runtime-resume errors through err_out. Later component resume stages remain skipped. The original error still reaches USB core. A later transfer can fail if the device did not recover.
I reproduced the system-resume failure on an Audient iD14 MkI with an out-of-tree diagnostic mixer resume hook. An injected -EIO on the unpatched core left control readers in uninterruptible sleep in snd_power_ref_and_wait() until a reboot. With this patch, the same failure restored control access. A second system suspend and resume also succeeded after I disabled fault injection.
--- sound/usb/card.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
sound/usb/card.c +10 -4
@@ -1047,8 +1047,11 @@ static int usb_audio_resume(struct usb_interface *intf)list_for_each_entry (as, &chip->pcm_list, list) { err =snd_usb_pcm_resume (as); -if (err <0 ) - goto err_out; +if (err <0 ) { +if (!chip->system_suspend) + goto err_out; + goto out; + } }/*
@@ -1057,8 +1060,11 @@ static int usb_audio_resume(struct usb_interface *intf)*/ list_for_each_entry (mixer, &chip->mixer_list, list) { err =snd_usb_mixer_resume (mixer); -if (err <0 ) - goto err_out; +if (err <0 ) { +if (!chip->system_suspend) + goto err_out; + goto out; + } }list_for_each (p, &chip->midi_list) {
Signature · 1 line
2.53.0