diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 02d8689354c4..1cf511f8d880 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -8,6 +8,7 @@ * Abramo Bagnara */ +#include #include #include #include @@ -414,6 +415,7 @@ struct snd_pcm_runtime { snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */ wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t tsleep; /* transfer sleep */ + struct snd_refcount refcnt; /* refcount for draining, etc */ struct snd_fasync *fasync; bool stop_operating; /* sync_stop will be called */ struct mutex buffer_mutex; /* protect for buffer changes */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 41c2cab7a52c..c133f23418fe 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -957,6 +957,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, init_waitqueue_head(&runtime->sleep); init_waitqueue_head(&runtime->tsleep); + snd_refcount_init(&runtime->refcnt); __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); mutex_init(&runtime->buffer_mutex); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7ffbf2c0ea92..05097f2630ad 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2226,9 +2226,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, for (;;) { long tout; struct snd_pcm_runtime *to_check; - unsigned int drain_rate; - snd_pcm_uframes_t drain_bufsz; - bool drain_no_period_wakeup; if (signal_pending(current)) { result = -ERESTARTSYS; @@ -2243,45 +2240,35 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, runtime = s->runtime; if (runtime->state == SNDRV_PCM_STATE_DRAINING) { to_check = runtime; + /* pin the draining substream */ + snd_refcount_get(&to_check->refcnt); + + init_wait_entry(&wait, 0); + prepare_to_wait(&to_check->sleep, &wait, + TASK_INTERRUPTIBLE); + if (to_check->no_period_wakeup) { + tout = MAX_SCHEDULE_TIMEOUT; + } else { + tout = 100; + if (to_check->rate) { + long t = to_check->buffer_size * + 1100 / to_check->rate; + tout = max(t, tout); + } + tout = msecs_to_jiffies(tout); + } break; } } snd_pcm_group_unref(group, substream); if (!to_check) break; /* all drained */ - /* - * Cache the runtime fields needed after unlock. - * A concurrent close() on the linked stream may free - * its runtime via snd_pcm_detach_substream() once we - * release the stream lock below. - */ - drain_no_period_wakeup = to_check->no_period_wakeup; - drain_rate = to_check->rate; - drain_bufsz = to_check->buffer_size; - init_wait_entry(&wait, 0); - prepare_to_wait(&to_check->sleep, &wait, TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); - if (drain_no_period_wakeup) - tout = MAX_SCHEDULE_TIMEOUT; - else { - tout = 100; - if (drain_rate) { - long t = drain_bufsz * 1100 / drain_rate; - tout = max(t, tout); - } - tout = msecs_to_jiffies(tout); - } tout = schedule_timeout(tout); - snd_pcm_stream_lock_irq(substream); - group = snd_pcm_stream_group_ref(substream); - snd_pcm_group_for_each_entry(s, substream) { - if (s->runtime == to_check) { - finish_wait(&to_check->sleep, &wait); - break; - } - } - snd_pcm_group_unref(group, substream); + finish_wait(&to_check->sleep, &wait); + /* unpin the draining substream again now */ + snd_refcount_put(&to_check->refcnt); if (card->shutdown) { result = -ENODEV; @@ -2835,6 +2822,7 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) snd_pcm_drop(substream); if (substream->hw_opened) { + snd_refcount_sync(&substream->runtime->refcnt); if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) do_hw_free(substream); substream->ops->close(substream);