[PATCH v2 2/3] ALSA: pcm: oss: Retain the PCM from minor lookup through release
From: Cen Zhang
Date: Sat Oct 24 2026 - 07:42:06 EST
An OSS open uses the PCM before acquiring open_mutex and before the HDA
driver's open callback takes a codec PCM reference. In particular,
snd_pcm_oss_look_for_setup() walks the stream's setup list during this
interval. The lookup's card reference does not retain a PCM component
removed by codec-only unbind on a registered card:
OSS open HDA codec unbind
-------- ----------------
lookup OSS minor
retain card reference
snd_pcm_oss_look_for_setup()
disconnect PCM
wait for codec PCM references
snd_device_free()
snd_pcm_free()
use setup list or PCM in open
The OSS module pin retains its code, and proc disconnect drains proc
callbacks; neither retains the PCM for this early OSS open.
Use the PCM core's lookup-and-retain helper for the OSS minor. Hold the
PCM reference through the early setup lookup and transfer it to the file
on success. Drop it on every open failure or after substream release,
before the last card reference can invoke final card destruction.
The final PCM free now waits for that early opener before removing OSS
setup lists. A pending opener which resumes after disconnect is rejected
before entering the driver, and a waiting OSS opener observes the PCM's
component disconnection as well as whole-card shutdown.
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@xxxxxxxxx>
---
include/sound/pcm.h | 2 ++
sound/core/oss/pcm_oss.c | 20 +++++++++++++-------
sound/core/pcm.c | 15 +++++++++++++++
3 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8fdb07f2f542..12559c2144cf 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -577,6 +577,8 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count);
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+struct snd_pcm *snd_pcm_get_by_oss_minor(unsigned int minor);
+
struct snd_pcm_notify {
int (*n_register) (struct snd_pcm * pcm);
int (*n_disconnect) (struct snd_pcm * pcm);
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index e8df27d09b97..9f0dae051ecf 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -2497,8 +2497,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
nonseekable_open(inode, file);
- pcm = snd_lookup_oss_minor_data(iminor(inode),
- SNDRV_OSS_DEVICE_TYPE_PCM);
+ pcm = snd_pcm_get_by_oss_minor(iminor(inode));
if (pcm == NULL) {
err = -ENODEV;
goto __error1;
@@ -2545,7 +2544,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
- if (pcm->card->shutdown) {
+ if (pcm->card->shutdown || pcm->disconnected) {
err = -ENODEV;
break;
}
@@ -2566,14 +2565,19 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
- if (pcm)
- snd_card_unref(pcm->card);
+ if (pcm) {
+ struct snd_card *card = pcm->card;
+
+ snd_pcm_put(pcm);
+ snd_card_unref(card);
+ }
return err;
}
static int snd_pcm_oss_release(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
+ struct snd_card *card;
struct snd_pcm_substream *substream;
struct snd_pcm_oss_file *pcm_oss_file;
@@ -2584,14 +2588,16 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file)
if (snd_BUG_ON(!substream))
return -ENXIO;
pcm = substream->pcm;
+ card = pcm->card;
if (!pcm->card->shutdown)
snd_pcm_oss_sync(pcm_oss_file);
mutex_lock(&pcm->open_mutex);
snd_pcm_oss_release_file(pcm_oss_file);
mutex_unlock(&pcm->open_mutex);
wake_up(&pcm->open_wait);
- module_put(pcm->card->module);
- snd_card_file_remove(pcm->card, file);
+ module_put(card->module);
+ snd_pcm_put(pcm);
+ snd_card_file_remove(card, file);
return 0;
}
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 17cd60e8825d..380ce0781342 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1064,6 +1064,21 @@ struct snd_pcm *snd_pcm_get_by_minor(unsigned int minor, int type)
}
EXPORT_SYMBOL_GPL(snd_pcm_get_by_minor);
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+/**
+ * snd_pcm_get_by_oss_minor - look up and retain an OSS PCM component
+ * @minor: OSS minor number
+ *
+ * Returns the same PCM and card references as snd_pcm_get_by_minor().
+ */
+struct snd_pcm *snd_pcm_get_by_oss_minor(unsigned int minor)
+{
+ return pcm_get_from_minor(minor, SNDRV_OSS_DEVICE_TYPE_PCM,
+ snd_lookup_oss_minor_data);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_get_by_oss_minor);
+#endif
+
static ssize_t pcm_class_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
--
2.43.0