* [PATCH] ALSA: ymfpci: avoid changing control access under spinlock
@ 2026-09-27 13:53 Runyu Xiao
2026-09-28 12:54 ` Takashi Iwai
0 siblings, 1 reply; 2+ messages in thread
From: Runyu Xiao @ 2026-09-27 13:53 UTC (permalink / raw)
To: Jaroslav Kysela
Cc: Takashi Iwai, Clemens Ladisch, Runyu Xiao, Jianhao Xu,
linux-sound, linux-kernel
The YMFPCI playback trigger changes a mixer control's access flags while
holding reg_lock. Direct access changes are not serialized with the control
core, and snd_ctl_activate_id() cannot be called under the spinlock because
it may sleep.
Track the desired control state under reg_lock and update it from a work
item. Use a generation counter to serialize prepare with a pending stop.
Cancel the work before the PCM private data is released.
Assisted-by: LLM
Fixes: 177a7cdbd1d8 ("[ALSA] ymfpci: fix volume handling of the 44.1 kHz slot")
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
sound/pci/ymfpci/ymfpci.h | 4 ++
sound/pci/ymfpci/ymfpci_main.c | 69 ++++++++++++++++++++++++++--------
2 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h
index a408785cf..33c7d4327 100644
--- a/sound/pci/ymfpci/ymfpci.h
+++ b/sound/pci/ymfpci/ymfpci.h
@@ -12,6 +12,7 @@
#include <sound/ac97_codec.h>
#include <sound/timer.h>
#include <linux/gameport.h>
+#include <linux/workqueue.h>
/*
* Direct registers
@@ -260,6 +261,9 @@ struct snd_ymfpci_pcm {
output_rear: 1,
swap_rear: 1;
unsigned int update_pcm_vol;
+ struct work_struct control_work;
+ unsigned int control_generation;
+ bool control_active;
u32 period_size; /* cached from runtime->period_size */
u32 buffer_size; /* cached from runtime->buffer_size */
u32 period_pos;
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 2ccb976e6..e5c0fb78e 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -355,12 +355,52 @@ static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream
}
}
+static void snd_ymfpci_pcm_control_work(struct work_struct *work)
+{
+ struct snd_ymfpci_pcm *ypcm = container_of(work, struct snd_ymfpci_pcm,
+ control_work);
+ struct snd_ymfpci *chip = ypcm->chip;
+ struct snd_kcontrol *kctl;
+ unsigned int generation;
+ bool active;
+
+ kctl = chip->pcm_mixer[ypcm->substream->number].ctl;
+ if (!kctl)
+ return;
+
+ for (;;) {
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ active = ypcm->control_active;
+ generation = ypcm->control_generation;
+ }
+ snd_ctl_activate_id(chip->card, &kctl->id, active);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ if (generation == ypcm->control_generation)
+ return;
+ }
+ }
+}
+
+static void snd_ymfpci_pcm_set_control_active(struct snd_ymfpci_pcm *ypcm,
+ bool active)
+{
+ struct snd_ymfpci *chip = ypcm->chip;
+ struct snd_kcontrol *kctl;
+
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ ypcm->control_active = active;
+ ypcm->control_generation++;
+ }
+ kctl = chip->pcm_mixer[ypcm->substream->number].ctl;
+ if (kctl)
+ snd_ctl_activate_id(chip->card, &kctl->id, active);
+}
+
static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
- struct snd_kcontrol *kctl = NULL;
int result = 0;
guard(spinlock)(&chip->reg_lock);
@@ -377,8 +417,9 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_STOP:
if (substream->pcm == chip->pcm && !ypcm->use_441_slot) {
- kctl = chip->pcm_mixer[substream->number].ctl;
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ypcm->control_active = false;
+ ypcm->control_generation++;
+ schedule_work(&ypcm->control_work);
}
fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -391,8 +432,6 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- if (kctl)
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
return result;
}
static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
@@ -636,7 +675,6 @@ static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
- struct snd_kcontrol *kctl;
unsigned int nvoice;
ypcm->period_size = runtime->period_size;
@@ -648,9 +686,7 @@ static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
substream->pcm == chip->pcm);
if (substream->pcm == chip->pcm && !ypcm->use_441_slot) {
- kctl = chip->pcm_mixer[substream->number].ctl;
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+ snd_ymfpci_pcm_set_control_active(ypcm, true);
}
return 0;
}
@@ -843,7 +879,10 @@ static const struct snd_pcm_hardware snd_ymfpci_capture =
static void snd_ymfpci_pcm_free_substream(struct snd_pcm_runtime *runtime)
{
- kfree(runtime->private_data);
+ struct snd_ymfpci_pcm *ypcm = runtime->private_data;
+
+ cancel_work_sync(&ypcm->control_work);
+ kfree(ypcm);
}
static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
@@ -870,6 +909,7 @@ static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
ypcm->chip = chip;
ypcm->type = PLAYBACK_VOICE;
ypcm->substream = substream;
+ INIT_WORK(&ypcm->control_work, snd_ymfpci_pcm_control_work);
runtime->private_data = ypcm;
runtime->private_free = snd_ymfpci_pcm_free_substream;
return 0;
@@ -945,9 +985,7 @@ static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream)
chip->spdif_opened++;
}
- chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+ snd_ctl_activate_id(chip->card, &chip->spdif_pcm_ctl->id, 1);
return 0;
}
@@ -997,6 +1035,7 @@ static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
ypcm->type = capture_bank_number + CAPTURE_REC;
ypcm->substream = substream;
ypcm->capture_bank_number = capture_bank_number;
+ INIT_WORK(&ypcm->control_work, snd_ymfpci_pcm_control_work);
chip->capture_substream[capture_bank_number] = substream;
runtime->private_data = ypcm;
runtime->private_free = snd_ymfpci_pcm_free_substream;
@@ -1044,9 +1083,7 @@ static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream)
snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
}
- chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+ snd_ctl_activate_id(chip->card, &chip->spdif_pcm_ctl->id, 0);
return snd_ymfpci_playback_close_1(substream);
}
--
2.34.1
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] ALSA: ymfpci: avoid changing control access under spinlock
2026-09-27 13:53 [PATCH] ALSA: ymfpci: avoid changing control access under spinlock Runyu Xiao
@ 2026-09-28 12:54 ` Takashi Iwai
0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2026-09-28 12:54 UTC (permalink / raw)
To: Runyu Xiao
Cc: Jaroslav Kysela, Takashi Iwai, Clemens Ladisch, Jianhao Xu,
linux-sound, linux-kernel
On Sun, 27 Sep 2026 15:53:01 +0200,
Runyu Xiao wrote:
>
> The YMFPCI playback trigger changes a mixer control's access flags while
> holding reg_lock. Direct access changes are not serialized with the control
> core, and snd_ctl_activate_id() cannot be called under the spinlock because
> it may sleep.
>
> Track the desired control state under reg_lock and update it from a work
> item. Use a generation counter to serialize prepare with a pending stop.
> Cancel the work before the PCM private data is released.
>
> Assisted-by: LLM
> Fixes: 177a7cdbd1d8 ("[ALSA] ymfpci: fix volume handling of the 44.1 kHz slot")
> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
Although the intention is clear, this is just an overkill. There is
no real race, and introducing the offloading work just for using
snd_ctl_activate_id() makes little sense for this old code.
If any, we should rather make snd_ctl_activate_id() to be callable
from the irq context, instead. But this would need a redesign of the
control-led layer implementation -- that's the very reason of being
sleepable context.
thanks,
Takashi
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-28 12:54 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-27 13:53 [PATCH] ALSA: ymfpci: avoid changing control access under spinlock Runyu Xiao
2026-09-28 12:54 ` Takashi Iwai
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®