]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ALSA: rawmidi: Take buffer refcount while draining output
authorTakashi Iwai <tiwai@suse.de>
Fri, 17 Jun 2022 14:40:51 +0000 (16:40 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 20 Jun 2022 07:36:04 +0000 (09:36 +0200)
Although snd_rawmidi_drain_output() may take some long time, it has no
protection and intrusive operations like the buffer resize may happen
meanwhile.  For making the operation a bit more robust, this patch
takes the buffer refcount for blocking the buffer resize.

Also, as this function is exported, in theory, it might be called
asynchronously from the stream open/close state.  For avoiding the
missing refcount, now the close call checks the buffer refcount, too.

Link: https://lore.kernel.org/r/20220617144051.18985-6-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/core/rawmidi.c

index 889fa4747dad4f9fd22e6abdf77165e2a22ff668..6963d5a487b3278dbaa8cbf029a9dde9b3966066 100644 (file)
@@ -140,6 +140,23 @@ static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
        runtime->buffer_ref--;
 }
 
+static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
+{
+       int loop = HZ;
+
+       spin_lock_irq(&substream->lock);
+       while (substream->runtime->buffer_ref) {
+               spin_unlock_irq(&substream->lock);
+               if (!--loop) {
+                       rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
+                       return;
+               }
+               schedule_timeout_uninterruptible(1);
+               spin_lock_irq(&substream->lock);
+       }
+       spin_unlock_irq(&substream->lock);
+}
+
 static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
 {
        struct snd_rawmidi_runtime *runtime;
@@ -222,15 +239,27 @@ EXPORT_SYMBOL(snd_rawmidi_drop_output);
 
 int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
 {
-       int err;
+       int err = 0;
        long timeout;
-       struct snd_rawmidi_runtime *runtime = substream->runtime;
+       struct snd_rawmidi_runtime *runtime;
+
+       spin_lock_irq(&substream->lock);
+       runtime = substream->runtime;
+       if (!substream->opened || !runtime || !runtime->buffer) {
+               err = -EINVAL;
+       } else {
+               snd_rawmidi_buffer_ref(runtime);
+               runtime->drain = 1;
+       }
+       spin_unlock_irq(&substream->lock);
+       if (err < 0)
+               return err;
 
-       err = 0;
-       runtime->drain = 1;
        timeout = wait_event_interruptible_timeout(runtime->sleep,
                                (runtime->avail >= runtime->buffer_size),
                                10*HZ);
+
+       spin_lock_irq(&substream->lock);
        if (signal_pending(current))
                err = -ERESTARTSYS;
        if (runtime->avail < runtime->buffer_size && !timeout) {
@@ -240,6 +269,8 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
                err = -EIO;
        }
        runtime->drain = 0;
+       spin_unlock_irq(&substream->lock);
+
        if (err != -ERESTARTSYS) {
                /* we need wait a while to make sure that Tx FIFOs are empty */
                if (substream->ops->drain)
@@ -248,6 +279,11 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
                        msleep(50);
                snd_rawmidi_drop_output(substream);
        }
+
+       spin_lock_irq(&substream->lock);
+       snd_rawmidi_buffer_unref(runtime);
+       spin_unlock_irq(&substream->lock);
+
        return err;
 }
 EXPORT_SYMBOL(snd_rawmidi_drain_output);
@@ -522,6 +558,7 @@ static void close_substream(struct snd_rawmidi *rmidi,
                        if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
                                snd_rawmidi_output_trigger(substream, 0);
                }
+               snd_rawmidi_buffer_ref_sync(substream);
        }
        spin_lock_irq(&substream->lock);
        substream->opened = 0;