These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / core / pcm_native.c
index ec8486c..34e5018 100644 (file)
@@ -74,6 +74,18 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
 static DEFINE_RWLOCK(snd_pcm_link_rwlock);
 static DECLARE_RWSEM(snd_pcm_link_rwsem);
 
+/* Writer in rwsem may block readers even during its waiting in queue,
+ * and this may lead to a deadlock when the code path takes read sem
+ * twice (e.g. one in snd_pcm_action_nonatomic() and another in
+ * snd_pcm_stream_lock()).  As a (suboptimal) workaround, let writer to
+ * spin until it gets the lock.
+ */
+static inline void down_write_nonblock(struct rw_semaphore *lock)
+{
+       while (!down_write_trylock(lock))
+               cond_resched();
+}
+
 /**
  * snd_pcm_stream_lock - Lock the PCM stream
  * @substream: PCM substream
@@ -486,6 +498,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
        snd_pcm_stream_unlock_irq(substream);
 }
 
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+                                       int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+       if (substream->timer)
+               snd_timer_notify(substream->timer, event,
+                                       &substream->runtime->trigger_tstamp);
+#endif
+}
+
 static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -650,7 +672,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        }
        snd_pcm_stream_unlock_irq(substream);
 
-       if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+       if (params->tstamp_mode < 0 ||
+           params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
                return -EINVAL;
        if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
            params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1065,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, ULONG_MAX);
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
 }
 
 static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1113,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
        if (runtime->status->state != state) {
                snd_pcm_trigger_tstamp(substream);
                runtime->status->state = state;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
        }
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
@@ -1208,18 +1227,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
        snd_pcm_trigger_tstamp(substream);
        if (push) {
                runtime->status->state = SNDRV_PCM_STATE_PAUSED;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MPAUSE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
                wake_up(&runtime->sleep);
                wake_up(&runtime->tsleep);
        } else {
                runtime->status->state = SNDRV_PCM_STATE_RUNNING;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MCONTINUE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
        }
 }
 
@@ -1267,9 +1280,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
        snd_pcm_trigger_tstamp(substream);
        runtime->status->suspended_state = runtime->status->state;
        runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
 }
@@ -1373,9 +1384,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_trigger_tstamp(substream);
        runtime->status->state = runtime->status->suspended_state;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
 }
 
 static struct action_ops snd_pcm_action_resume = {
@@ -1816,7 +1825,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
                res = -ENOMEM;
                goto _nolock;
        }
-       down_write(&snd_pcm_link_rwsem);
+       down_write_nonblock(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
            substream->runtime->status->state != substream1->runtime->status->state ||
@@ -1863,7 +1872,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
        struct snd_pcm_substream *s;
        int res = 0;
 
-       down_write(&snd_pcm_link_rwsem);
+       down_write_nonblock(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (!snd_pcm_stream_linked(substream)) {
                res = -EALREADY;
@@ -2226,7 +2235,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
 
        snd_pcm_drop(substream);
        if (substream->hw_opened) {
-               if (substream->ops->hw_free != NULL)
+               if (substream->ops->hw_free &&
+                   substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
                        substream->ops->hw_free(substream);
                substream->ops->close(substream);
                substream->hw_opened = 0;