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..43f765300031 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 registered; }; #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..e8df27d09b97 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.registered) + return 0; + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + pcm->oss.registered = 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.registered) + return 0; + snd_pcm_oss_disconnect_minor(pcm); snd_pcm_oss_proc_done(pcm); + pcm->oss.registered = false; + module_put(THIS_MODULE); return 0; }