ALSA: usb-audio: Release components on probe errors

usb_audio_probe() can create USB-audio component resources before the
first interface is recorded in chip->num_interfaces.  If a later probe
step fails, the error path drops chip->active and frees the card directly
when no interfaces have been registered.

Normal disconnect first releases USB-audio components in a fixed order
before the card is freed.  The first-interface probe error path skipped
that sequence, so partially initialized PCM, endpoint, MIDI, media, or
mixer resources could be left for the card private_free path without their
disconnect handling having run.

Move the existing normal-disconnect component release sequence into a
helper and call it from the zero-interface probe error path before
snd_card_free().  Keep the normal disconnect ordering unchanged: PCM
streams, endpoint resources, MIDI 1.0 resources, MIDI 2.0 resources, media
device cleanup, then mixer resources.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Link: https://patch.msgid.link/20260618170026.192212-1-zzzccc427@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Cen Zhang 2026-06-19 01:00:26 +08:00 committed by Takashi Iwai
parent e096c24e7f
commit 3213bf9885

View File

@ -905,6 +905,40 @@ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum)
return 0;
}
static void usb_audio_disconnect_components(struct snd_usb_audio *chip)
{
struct snd_usb_stream *as;
struct snd_usb_endpoint *ep;
struct usb_mixer_interface *mixer;
struct list_head *p;
/* release the pcm resources */
list_for_each_entry(as, &chip->pcm_list, list) {
snd_usb_stream_disconnect(as);
}
/* release the endpoint resources */
list_for_each_entry(ep, &chip->ep_list, list) {
snd_usb_endpoint_release(ep);
}
/* release the midi resources */
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
snd_usb_midi_v2_disconnect_all(chip);
/*
* Nice to check quirk && quirk->shares_media_device and
* then call the snd_media_device_delete(). Don't have
* access to the quirk here. snd_media_device_delete()
* accesses mixer_list
*/
snd_media_device_delete(chip);
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
}
}
/*
* probe the active usb device
*
@ -1077,8 +1111,10 @@ static int usb_audio_probe(struct usb_interface *intf,
* decrement before memory is possibly returned.
*/
atomic_dec(&chip->active);
if (!chip->num_interfaces)
if (!chip->num_interfaces) {
usb_audio_disconnect_components(chip);
snd_card_free(chip->card);
}
}
return err;
}
@ -1091,48 +1127,18 @@ static bool __usb_audio_disconnect(struct usb_interface *intf,
struct snd_usb_audio *chip,
struct snd_card *card)
{
struct list_head *p;
guard(mutex)(&register_mutex);
if (platform_ops && platform_ops->disconnect_cb)
platform_ops->disconnect_cb(chip);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
struct snd_usb_endpoint *ep;
struct usb_mixer_interface *mixer;
/* wait until all pending tasks done;
* they are protected by snd_usb_lock_shutdown()
*/
snd_refcount_sync(&chip->usage_count);
snd_card_disconnect(card);
/* release the pcm resources */
list_for_each_entry(as, &chip->pcm_list, list) {
snd_usb_stream_disconnect(as);
}
/* release the endpoint resources */
list_for_each_entry(ep, &chip->ep_list, list) {
snd_usb_endpoint_release(ep);
}
/* release the midi resources */
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
snd_usb_midi_v2_disconnect_all(chip);
/*
* Nice to check quirk && quirk->shares_media_device and
* then call the snd_media_device_delete(). Don't have
* access to the quirk here. snd_media_device_delete()
* accesses mixer_list
*/
snd_media_device_delete(chip);
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
}
usb_audio_disconnect_components(chip);
}
if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND)