ALSA: core: Remove debugfs at disconnection
The card-specific debugfs entries are removed at the last stage of
card free phase, and it's performed after synchronization of the
closes of all opened fds. This works fine for most cases, but it can
be potentially problematic for a hotplug device like USB-audio. Due
to the nature of snd_card_free_when_closed(), the card free isn't
called immediately after the driver removal for a hotplug device, but
it's left until the last fd is closed. It implies that the card
debugfs entries also remain. Meanwhile, when a new device is inserted
before the last close and the very same card slot is assigned, the
driver tries to create the card debugfs root again on the very same
path. This conflicts with the remaining entry, and results in the
kernel warning such as:
debugfs: Directory 'card0' with parent 'sound' already present!
with the missing debugfs entry afterwards.
For avoiding such conflicts, remove debugfs entries at the device
disconnection phase instead. The jack kctl debugfs entries get
removed in snd_jack_dev_disconnect() instead of each kctl
private_free.
Fixes: 2d670ea2bd
("ALSA: jack: implement software jack injection via debugfs")
Link: https://lore.kernel.org/r/20240524151256.32521-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
1613e604df
commit
495000a386
@ -537,6 +537,11 @@ void snd_card_disconnect(struct snd_card *card)
|
|||||||
synchronize_irq(card->sync_irq);
|
synchronize_irq(card->sync_irq);
|
||||||
|
|
||||||
snd_info_card_disconnect(card);
|
snd_info_card_disconnect(card);
|
||||||
|
#ifdef CONFIG_SND_DEBUG
|
||||||
|
debugfs_remove(card->debugfs_root);
|
||||||
|
card->debugfs_root = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (card->registered) {
|
if (card->registered) {
|
||||||
device_del(&card->card_dev);
|
device_del(&card->card_dev);
|
||||||
card->registered = false;
|
card->registered = false;
|
||||||
@ -586,10 +591,6 @@ static int snd_card_do_free(struct snd_card *card)
|
|||||||
dev_warn(card->dev, "unable to free card info\n");
|
dev_warn(card->dev, "unable to free card info\n");
|
||||||
/* Not fatal error */
|
/* Not fatal error */
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_SND_DEBUG
|
|
||||||
debugfs_remove(card->debugfs_root);
|
|
||||||
card->debugfs_root = NULL;
|
|
||||||
#endif
|
|
||||||
if (card->release_completion)
|
if (card->release_completion)
|
||||||
complete(card->release_completion);
|
complete(card->release_completion);
|
||||||
if (!card->managed)
|
if (!card->managed)
|
||||||
|
@ -37,11 +37,15 @@ static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
|
|||||||
};
|
};
|
||||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||||
|
|
||||||
|
static void snd_jack_remove_debugfs(struct snd_jack *jack);
|
||||||
|
|
||||||
static int snd_jack_dev_disconnect(struct snd_device *device)
|
static int snd_jack_dev_disconnect(struct snd_device *device)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
|
||||||
struct snd_jack *jack = device->device_data;
|
struct snd_jack *jack = device->device_data;
|
||||||
|
|
||||||
|
snd_jack_remove_debugfs(jack);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||||
guard(mutex)(&jack->input_dev_lock);
|
guard(mutex)(&jack->input_dev_lock);
|
||||||
if (!jack->input_dev)
|
if (!jack->input_dev)
|
||||||
return 0;
|
return 0;
|
||||||
@ -381,10 +385,14 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl)
|
static void snd_jack_remove_debugfs(struct snd_jack *jack)
|
||||||
{
|
{
|
||||||
debugfs_remove(jack_kctl->jack_debugfs_root);
|
struct snd_jack_kctl *jack_kctl;
|
||||||
jack_kctl->jack_debugfs_root = NULL;
|
|
||||||
|
list_for_each_entry(jack_kctl, &jack->kctl_list, list) {
|
||||||
|
debugfs_remove(jack_kctl->jack_debugfs_root);
|
||||||
|
jack_kctl->jack_debugfs_root = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else /* CONFIG_SND_JACK_INJECTION_DEBUG */
|
#else /* CONFIG_SND_JACK_INJECTION_DEBUG */
|
||||||
static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
|
static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
|
||||||
@ -393,7 +401,7 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl)
|
static void snd_jack_remove_debugfs(struct snd_jack *jack)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SND_JACK_INJECTION_DEBUG */
|
#endif /* CONFIG_SND_JACK_INJECTION_DEBUG */
|
||||||
@ -404,7 +412,6 @@ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
|
|||||||
|
|
||||||
jack_kctl = kctl->private_data;
|
jack_kctl = kctl->private_data;
|
||||||
if (jack_kctl) {
|
if (jack_kctl) {
|
||||||
snd_jack_debugfs_clear_inject_node(jack_kctl);
|
|
||||||
list_del(&jack_kctl->list);
|
list_del(&jack_kctl->list);
|
||||||
kfree(jack_kctl);
|
kfree(jack_kctl);
|
||||||
}
|
}
|
||||||
@ -497,8 +504,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
|
|||||||
.dev_free = snd_jack_dev_free,
|
.dev_free = snd_jack_dev_free,
|
||||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||||
.dev_register = snd_jack_dev_register,
|
.dev_register = snd_jack_dev_register,
|
||||||
.dev_disconnect = snd_jack_dev_disconnect,
|
|
||||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||||
|
.dev_disconnect = snd_jack_dev_disconnect,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (initial_kctl) {
|
if (initial_kctl) {
|
||||||
|
Loading…
Reference in New Issue
Block a user