Commit 495000a3 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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: 2d670ea2 ("ALSA: jack: implement software jack injection via debugfs")
Link: https://lore.kernel.org/r/20240524151256.32521-1-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1613e604
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -537,6 +537,11 @@ void snd_card_disconnect(struct snd_card *card)
		synchronize_irq(card->sync_irq);

	snd_info_card_disconnect(card);
#ifdef CONFIG_SND_DEBUG
	debugfs_remove(card->debugfs_root);
	card->debugfs_root = NULL;
#endif

	if (card->registered) {
		device_del(&card->card_dev);
		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");
		/* Not fatal error */
	}
#ifdef CONFIG_SND_DEBUG
	debugfs_remove(card->debugfs_root);
	card->debugfs_root = NULL;
#endif
	if (card->release_completion)
		complete(card->release_completion);
	if (!card->managed)
+14 −7
Original line number Diff line number Diff line
@@ -37,11 +37,15 @@ static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
};
#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)
{
#ifdef CONFIG_SND_JACK_INPUT_DEV
	struct snd_jack *jack = device->device_data;

	snd_jack_remove_debugfs(jack);

#ifdef CONFIG_SND_JACK_INPUT_DEV
	guard(mutex)(&jack->input_dev_lock);
	if (!jack->input_dev)
		return 0;
@@ -381,11 +385,15 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
	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)
{
	struct snd_jack_kctl *jack_kctl;

	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 */
static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
					    struct snd_jack_kctl *jack_kctl)
@@ -393,7 +401,7 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
	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 */
@@ -404,7 +412,6 @@ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)

	jack_kctl = kctl->private_data;
	if (jack_kctl) {
		snd_jack_debugfs_clear_inject_node(jack_kctl);
		list_del(&jack_kctl->list);
		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,
#ifdef CONFIG_SND_JACK_INPUT_DEV
		.dev_register = snd_jack_dev_register,
		.dev_disconnect = snd_jack_dev_disconnect,
#endif /* CONFIG_SND_JACK_INPUT_DEV */
		.dev_disconnect = snd_jack_dev_disconnect,
	};

	if (initial_kctl) {