Re: [PATCH] ALSA: seq: midi: Serialize input teardown with event_input

Takashi Iwai <[email protected]>
Newsgroups org.kernel.vger.linux-sound,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Thu, 13 Aug 2026 15:31:27 +0200,
John Keeping wrote:
> 
> snd_midi_input_event() must not be running while a rawmidi substream is
> closing, since this can lead to the trigger state becoming out-of-step
> through this sequence in snd_rawmidi_input_trigger():
> 
> 	snd_rawmidi_input_trigger(up=0)
> 					snd_midi_input_event()
> 					 -> snd_rawmidi_kernel_read()
> 					     -> snd_rawmidi_input_trigger(up=1)
> 	  -> cancel_work_sync()
> 
> which ends with the underlying device being active unexpectedly.
> 
> When this is called from close_substream(), further input can re-trigger
> the input event leaving it running after rawmidi_release_priv() has set
> rfile->rmidi to NULL which leads to:
> 
> 	Unable to handle kernel NULL pointer dereference at virtual address 00000000000000b0
> 	Call trace:
> 	 snd_midi_input_event+0x3c/0x134 [snd_seq_midi] (P)
> 	 snd_rawmidi_input_event_work+0x1c/0x2c
> 	 process_one_work+0x150/0x3a4
> 	 worker_thread+0x190/0x318
> 
> Apply the same approach as commit ef7607ab1c8ad ("ALSA: seq: midi:
> Serialize output teardown with event_input") which fixed the same issue
> in the output direction.
> 
> With this change in place, midisynth_unsubscribe() clears the input file
> so snd_midi_input_event() will not re-trigger the stream and will be
> quiesced by the cancel_work_sync() in snd_rawmidi_input_trigger().
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: John Keeping <[email protected]>

Thanks for the patch.  The fix itself looks OK, but I already have an
optimization of the previous output-teardown fix with RCU (which was
planned to be submitted in today), as attached below.  Could you try
to rewrite to fit with this form?


Takashi

-- 8< --
From: Takashi Iwai <[email protected]>
Subject: [PATCH] ALSA: seq: midi: Optimize event_input locking with RCU

The recent fix for serializing the output teardown introduced a
spinlock invocation at every MIDI output event via event_process_midi.
Since this is a hot path, let's do performance optimization with RCU.

The new output_substream __rcu pointer is published via
rcu_assign_pointer() in midisynth_use() after output_rfile is set, and
cleared in midisynth_unuse() before the resource teardown.
event_process_midi() reads it under rcu_read_lock() and bumps
output_use_lock inside that section, which is necessary to close the
window between the pointer dereference and the refcount increment.

midisynth_unuse() calls synchronize_rcu() before snd_use_lock_sync():
this guarantees that any reader who obtained a non-NULL pointer has
already called atomic_inc (output_use_lock), so the subsequent
snd_use_lock_sync() sees the correct in-flight count.

Signed-off-by: Takashi Iwai <[email protected]>
---
 sound/core/seq/seq_midi.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 2eb12199c92f..c2f89aee1914 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -43,8 +43,8 @@ struct seq_midisynth {
 	int device;
 	int subdevice;
 	struct snd_rawmidi_file input_rfile;
-	spinlock_t output_lock;		/* protects output_rfile publication */
 	snd_use_lock_t output_use_lock;	/* in-flight event_input users */
+	struct snd_rawmidi_substream __rcu *output_substream;
 	struct snd_rawmidi_file output_rfile;
 	int seq_client;
 	int seq_port;
@@ -134,8 +134,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
 	if (snd_BUG_ON(!msynth))
 		return -EINVAL;
 
-	scoped_guard(spinlock_irqsave, &msynth->output_lock) {
-		substream = msynth->output_rfile.output;
+	scoped_guard(rcu) {
+		substream = rcu_dereference(msynth->output_substream);
 		if (!substream)
 			return -ENODEV;
 		snd_use_lock_use(&msynth->output_use_lock);
@@ -177,7 +177,6 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
 	msynth->card = card;
 	msynth->device = device;
 	msynth->subdevice = subdevice;
-	spin_lock_init(&msynth->output_lock);
 	snd_use_lock_init(&msynth->output_use_lock);
 	return 0;
 }
@@ -252,8 +251,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
 		return err;
 	}
 	snd_midi_event_reset_decode(msynth->parser);
-	scoped_guard(spinlock_irqsave, &msynth->output_lock)
-		msynth->output_rfile = rfile;
+	msynth->output_rfile = rfile;
+	rcu_assign_pointer(msynth->output_substream, rfile.output);
 	return 0;
 }
 
@@ -261,17 +260,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
 static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 {
 	struct seq_midisynth *msynth = private_data;
-	struct snd_rawmidi_file rfile = {};
+	struct snd_rawmidi_file rfile;
 
-	scoped_guard(spinlock_irqsave, &msynth->output_lock) {
-		rfile = msynth->output_rfile;
-		msynth->output_rfile = (struct snd_rawmidi_file){};
-	}
+	rcu_assign_pointer(msynth->output_substream, NULL);
+	synchronize_rcu();
+	snd_use_lock_sync(&msynth->output_use_lock);
+	rfile = msynth->output_rfile;
+	msynth->output_rfile = (struct snd_rawmidi_file){};
 
 	if (snd_BUG_ON(!rfile.output))
 		return -EINVAL;
-
-	snd_use_lock_sync(&msynth->output_use_lock);
 	snd_rawmidi_drain_output(rfile.output);
 	return snd_rawmidi_kernel_release(&rfile);
 }
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.