From 9194d8dd4196fa3fb457de2909df468d501c01c2 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:51 +0800 Subject: [PATCH] ALSA: opl3: Shut down the timer after draining sequencer ports The delayed-note timer is independent of the sequencer ports' use-locks and runs code in snd-opl3-synth. Deleting the client drains the event callbacks but does not cancel this timer. A direct fixed-duration NOTEON without a subscription can arm it without any unuse callback to stop it. A shutdown in only the native port's private_free callback is too early to finish shared voice cleanup when the OSS port is enabled: Sequencer client deletion In-flight OSS NOTEON ------------------------- ------------------- drain native port clear native voice aliases snd_opl3_oss_event_input() refill shared voice array drain OSS port clear voice aliases again free OSS channel set client deletion returns The per-port voice cleanup prevents the timer from dereferencing a freed channel during that sequence, but a queued or running timer can still outlive client deletion and the sequencer driver itself. Shut the timer down synchronously after snd_seq_delete_kernel_client() has drained both ports. No event can then start it again, and shutdown waits for a callback already running and prevents callback self-rearming. Do this outside voice_lock and sys_timer_lock, which the callback takes. The probe initializes timer state before publishing ports on rebind. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-3@gmail.com --- sound/drivers/opl3/opl3_seq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index e03c88377bb6..1004a7df1b8c 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -259,6 +259,8 @@ static void snd_opl3_seq_remove(struct snd_seq_device *dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } + /* Stop the shared timer after all event callbacks have been drained. */ + timer_shutdown_sync(&opl3->tlist); } static struct snd_seq_driver opl3_seq_driver = {