mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Cen Zhang <zzzccc427@gmail.com>
Cc: perex@perex.cz, tiwai@suse.com, linux-sound@vger.kernel.org,
	linux-kernel@vger.kernel.org, tiwai@suse.de,
	baijiaju1990@gmail.com, jjzuming@gmail.com
Subject: Re: [PATCH v2] ALSA: oss: Keep emulation modules loaded until object teardown
Date: Sun, 18 Oct 2026 10:41:07 +0200	[thread overview]
Message-ID: <87zewbz9i4.wl-tiwai@suse.de> (raw)
In-Reply-To: <pm-pmb-26d66390a25fec9fad6c-v4-a7d8a5d3db28c5bca90a@gmail.com>

On Sat, 17 Oct 2026 18:04:28 +0200,
Cen Zhang wrote:
> 
> OSS proc entries and notifier callbacks can remain in use without an
> open OSS device file. Unloading snd-pcm-oss on a live card can remove a
> PCM's OSS proc entry while a repeated snd_card_register() walks the same
> entry tree:
> 
>   OSS PCM module unload                 Card registration
>   ---------------------                 -----------------
>   snd_pcm_oss_proc_done()
>     snd_info_free_entry(entry)
>       proc_remove(entry->p)
>       clear entry->p
>                                         snd_info_card_register()
>                                           snd_info_register()
>                                             republish entry
>       unlink and free entry
> 
> The new proc node then retains a pointer to freed storage. Mixer OSS
> also allows module exit to free an adapter while a card notification
> still uses it.
> 
> Following Takashi's suggestion, hold an OSS PCM module reference for
> each PCM registered with its notifier, and a mixer OSS module reference
> for each card adapter. Release them at final unregister or FREE, after
> the corresponding proc entries and adapter have been removed. Normal
> module unload can no longer initiate that removal on a live object.
> 
> Keep the PCM pin independent of oss.reg: disconnect clears that field,
> and unmapped PCMs also participate in notifier dispatch. Record whether
> a reference was acquired so BUILD-state and creation-error cleanup do
> not issue an unmatched module_put().
> 
> Protect the final PCM notifier traversal with register_mutex, matching
> notifier removal. For mixer OSS, serialize callback publication,
> withdrawal and dispatch in the core under a lifecycle mutex. Module exit
> then waits for a callback which dropped the last reference
> to return before withdrawing the callback and releasing module text.
> 
> Ignore duplicate mixer REGISTER notifications: module loading can find
> a card whose DISCONNECT has already withdrawn the mixer minor while
> retaining the adapter. Replacing it would leak the old adapter and pin.
> Keep existing card enumeration in the OSS module and drop its card
> references after the core dispatch helper returns, outside the mutex.
> The new locking is confined to lifecycle notifications.
> 
> Partial HDA codec teardown can still remove a PCM while its card stays
> registered; that object-lifetime problem requires separate treatment.
> 
> Fixes: 2471b6c80a70 ("ALSA: info: Register proc entries recursively, too")
> Suggested-by: Takashi Iwai <tiwai@suse.de>
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> Assisted-by: LLM
> ---
> Changes in v2:
> - Follow Takashi's suggestion to pin OSS modules until object teardown.
> - Track successful PCM pins across disconnect and unregistered cleanup.
> - Protect final PCM notifier traversal and mixer callback withdrawal across
>   the last module reference drop.
> - Keep card enumeration in the OSS module and prevent duplicate mixer
>   REGISTER notifications from replacing a disconnected adapter.
> - Leave partial HDA codec teardown for a separate fix.

Thanks, most of the stuff looks good, but a couple of points to
consider:

- Please split PCM and OSS code to individual patches.

- The new module_ref is a bit misleading name; it's no reference
  counter but just a boolean flag to indicate whether the PCM OSS
  stuff was registered or not.  Maybe 'bool registered' would be more
  intuitive?


thanks,

Takashi

> 
> Link to v1: https://lore.kernel.org/r/pm-pmb-26d66390a25fec9fad6c-v2-743c9b89c0f0d761eda1@gmail.com
> 
>  include/sound/core.h       |  3 ++-
>  include/sound/pcm_oss.h    |  1 +
>  sound/core/init.c          | 29 +++++++++++++++++++++--------
>  sound/core/oss/mixer_oss.c | 36 ++++++++++++++++++++----------------
>  sound/core/oss/pcm_oss.c   | 12 ++++++++++++
>  sound/core/pcm.c           |  6 ++++--
>  6 files changed, 60 insertions(+), 27 deletions(-)
> 
> diff --git a/include/sound/core.h b/include/sound/core.h
> index 657b12e1e95b..d6584967fd49 100644
> --- a/include/sound/core.h
> +++ b/include/sound/core.h
> @@ -301,7 +301,8 @@ int snd_card_locked(int card);
>  #define SND_MIXER_OSS_NOTIFY_REGISTER	0
>  #define SND_MIXER_OSS_NOTIFY_DISCONNECT	1
>  #define SND_MIXER_OSS_NOTIFY_FREE	2
> -extern int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int cmd);
> +void snd_mixer_oss_set_notify(int (*callback)(struct snd_card *card, int cmd));
> +void snd_mixer_oss_notify(struct snd_card *card, int cmd);
>  #endif
>  
>  int snd_card_new(struct device *parent, int idx, const char *xid,
> diff --git a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h
> index c96f23c7755b..fa78d7e3a3ee 100644
> --- a/include/sound/pcm_oss.h
> +++ b/include/sound/pcm_oss.h
> @@ -70,6 +70,7 @@ struct snd_pcm_oss_stream {
>  struct snd_pcm_oss {
>  	int reg;
>  	unsigned int reg_mask;
> +	bool module_ref;
>  };
>  
>  #endif /* __SOUND_PCM_OSS_H */
> diff --git a/sound/core/init.c b/sound/core/init.c
> index 9deb5e920d0d..d518cb838777 100644
> --- a/sound/core/init.c
> +++ b/sound/core/init.c
> @@ -82,8 +82,24 @@ static int module_slot_match(struct module *module, int idx)
>  }
>  
>  #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
> -int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
> -EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
> +static DEFINE_MUTEX(mixer_oss_notify_mutex);
> +static int (*mixer_oss_notify_callback)(struct snd_card *card, int cmd);
> +
> +void snd_mixer_oss_notify(struct snd_card *card, int cmd)
> +{
> +	guard(mutex)(&mixer_oss_notify_mutex);
> +	if (mixer_oss_notify_callback)
> +		mixer_oss_notify_callback(card, cmd);
> +}
> +EXPORT_SYMBOL(snd_mixer_oss_notify);
> +
> +void snd_mixer_oss_set_notify(int (*callback)(struct snd_card *, int))
> +{
> +	/* Wait for callbacks, including a final module_put(), to return. */
> +	guard(mutex)(&mixer_oss_notify_mutex);
> +	mixer_oss_notify_callback = callback;
> +}
> +EXPORT_SYMBOL(snd_mixer_oss_set_notify);
>  #endif
>  
>  static int check_empty_slot(struct module *module, int slot)
> @@ -508,8 +524,7 @@ void snd_card_disconnect(struct snd_card *card)
>  	/* at this point, they cannot respond to any calls except release() */
>  
>  #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
> -	if (snd_mixer_oss_notify_callback)
> -		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
> +	snd_mixer_oss_notify(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
>  #endif
>  
>  	/* notify all devices that we are disconnected */
> @@ -567,8 +582,7 @@ static int snd_card_do_free(struct snd_card *card)
>  
>  	card->releasing = true;
>  #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
> -	if (snd_mixer_oss_notify_callback)
> -		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
> +	snd_mixer_oss_notify(card, SND_MIXER_OSS_NOTIFY_FREE);
>  #endif
>  	snd_device_free_all(card);
>  	kfree(card->components);
> @@ -912,8 +926,7 @@ int snd_card_register(struct snd_card *card)
>  		return err;
>  
>  #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
> -	if (snd_mixer_oss_notify_callback)
> -		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
> +	snd_mixer_oss_notify(card, SND_MIXER_OSS_NOTIFY_REGISTER);
>  #endif
>  	return 0;
>  }
> diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
> index f1fa42e634fc..a51bc68473e6 100644
> --- a/sound/core/oss/mixer_oss.c
> +++ b/sound/core/oss/mixer_oss.c
> @@ -1309,13 +1309,22 @@ static int snd_mixer_oss_free1(void *private)
>  static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
>  {
>  	struct snd_mixer_oss *mixer;
> +	int err;
>  
>  	if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
> -		int idx, err;
> +		int idx;
> +
> +		/* Module loading and card registration can both notify us. */
> +		if (card->mixer_oss)
> +			return 0;
>  
>  		mixer = kzalloc_objs(*mixer, 2);
>  		if (mixer == NULL)
>  			return -ENOMEM;
> +		if (!try_module_get(THIS_MODULE)) {
> +			kfree(mixer);
> +			return -ENODEV;
> +		}
>  		mutex_init(&mixer->reg_mutex);
>  		err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
>  					      card, 0,
> @@ -1325,6 +1334,7 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
>  				"unable to register OSS mixer device %i:%i\n",
>  				card->number, 0);
>  			kfree(mixer);
> +			module_put(THIS_MODULE);
>  			return err;
>  		}
>  		mixer->oss_dev_alloc = 1;
> @@ -1358,7 +1368,10 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
>  		if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
>  			return 0;
>  		snd_mixer_oss_proc_done(mixer);
> -		return snd_mixer_oss_free1(mixer);
> +		err = snd_mixer_oss_free1(mixer);
> +		if (!err)
> +			module_put(THIS_MODULE);
> +		return err;
>  	}
>  	return 0;
>  }
> @@ -1367,12 +1380,12 @@ static int __init alsa_mixer_oss_init(void)
>  {
>  	struct snd_card *card;
>  	int idx;
> -	
> -	snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
> +
> +	snd_mixer_oss_set_notify(snd_mixer_oss_notify_handler);
>  	for (idx = 0; idx < SNDRV_CARDS; idx++) {
>  		card = snd_card_ref(idx);
>  		if (card) {
> -			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
> +			snd_mixer_oss_notify(card, SND_MIXER_OSS_NOTIFY_REGISTER);
>  			snd_card_unref(card);
>  		}
>  	}
> @@ -1381,17 +1394,8 @@ static int __init alsa_mixer_oss_init(void)
>  
>  static void __exit alsa_mixer_oss_exit(void)
>  {
> -	struct snd_card *card;
> -	int idx;
> -
> -	snd_mixer_oss_notify_callback = NULL;
> -	for (idx = 0; idx < SNDRV_CARDS; idx++) {
> -		card = snd_card_ref(idx);
> -		if (card) {
> -			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
> -			snd_card_unref(card);
> -		}
> -	}
> +	/* Live adapters hold module references and prevent normal unload. */
> +	snd_mixer_oss_set_notify(NULL);
>  }
>  
>  module_init(alsa_mixer_oss_init)
> diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
> index bd19328dca9e..cc239929dfca 100644
> --- a/sound/core/oss/pcm_oss.c
> +++ b/sound/core/oss/pcm_oss.c
> @@ -3151,6 +3151,12 @@ static void register_oss_dsp(struct snd_pcm *pcm, int index)
>  
>  static int snd_pcm_oss_register_minor(struct snd_pcm *pcm)
>  {
> +	if (pcm->oss.module_ref)
> +		return 0;
> +	if (!try_module_get(THIS_MODULE))
> +		return -ENODEV;
> +	pcm->oss.module_ref = true;
> +
>  	pcm->oss.reg = 0;
>  	if (dsp_map[pcm->card->number] == (int)pcm->device) {
>  		char name[128];
> @@ -3205,8 +3211,14 @@ static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)
>  
>  static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
>  {
> +	/* BUILD-state and creation-error cleanup need not have registered us. */
> +	if (!pcm->oss.module_ref)
> +		return 0;
> +
>  	snd_pcm_oss_disconnect_minor(pcm);
>  	snd_pcm_oss_proc_done(pcm);
> +	pcm->oss.module_ref = false;
> +	module_put(THIS_MODULE);
>  	return 0;
>  }
>  
> diff --git a/sound/core/pcm.c b/sound/core/pcm.c
> index c133f23418fe..35ece386d1a2 100644
> --- a/sound/core/pcm.c
> +++ b/sound/core/pcm.c
> @@ -853,8 +853,10 @@ static int snd_pcm_free(struct snd_pcm *pcm)
>  {
>  	if (!pcm)
>  		return 0;
> -	if (!pcm->internal)
> -		pcm_call_notify(pcm, n_unregister);
> +	if (!pcm->internal) {
> +		scoped_guard(mutex, &register_mutex)
> +			pcm_call_notify(pcm, n_unregister);
> +	}
>  	if (pcm->private_free)
>  		pcm->private_free(pcm);
>  	snd_pcm_lib_preallocate_free_for_all(pcm);
> 
> base-commit: 2533eecf592c2d7434170bf9669a4270fd594ae2
> -- 
> 2.43.0
> 

      reply	other threads:[~2026-10-18  8:41 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17 16:04 Cen Zhang
2026-10-18  8:41 ` Takashi Iwai [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zewbz9i4.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=baijiaju1990@gmail.com \
    --cc=jjzuming@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    --cc=zzzccc427@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®