Commit 5f615b97 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-4.15-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "A collection of the last-minute small PCM fixes:

   - A workaround for the recent regression wrt PulseAudio

   - Removal of spurious WARN_ON() that is triggered by syzkaller

   - Fixes for aloop, hardening racy accesses

   - Fixes in PCM OSS emulation wrt the unabortable loops that may cause
     RCU stall"

* tag 'sound-4.15-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: pcm: Allow aborting mutex lock at OSS read/write loops
  ALSA: pcm: Abort properly at pending signal in OSS read/write loops
  ALSA: aloop: Fix racy hw constraints adjustment
  ALSA: aloop: Fix inconsistent format due to incomplete rule
  ALSA: aloop: Release cable upon open error path
  ALSA: pcm: Workaround for weird PulseAudio behavior on rewind error
  ALSA: pcm: Add missing error checks in OSS emulation plugin builder
  ALSA: pcm: Remove incorrect snd_BUG_ON() usages
parents cf1fb158 900498a3
...@@ -455,7 +455,6 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, ...@@ -455,7 +455,6 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
v = snd_pcm_hw_param_last(pcm, params, var, dir); v = snd_pcm_hw_param_last(pcm, params, var, dir);
else else
v = snd_pcm_hw_param_first(pcm, params, var, dir); v = snd_pcm_hw_param_first(pcm, params, var, dir);
snd_BUG_ON(v < 0);
return v; return v;
} }
...@@ -1335,8 +1334,11 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha ...@@ -1335,8 +1334,11 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp; return tmp;
mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) { while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS;
break;
}
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes; tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
...@@ -1380,14 +1382,18 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha ...@@ -1380,14 +1382,18 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
xfer += tmp; xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 && if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes) tmp != runtime->oss.period_bytes)
break; tmp = -EAGAIN;
}
} }
mutex_unlock(&runtime->oss.params_lock);
return xfer;
err: err:
mutex_unlock(&runtime->oss.params_lock); mutex_unlock(&runtime->oss.params_lock);
if (tmp < 0)
break;
if (signal_pending(current)) {
tmp = -ERESTARTSYS;
break;
}
tmp = 0;
}
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
} }
...@@ -1435,8 +1441,11 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use ...@@ -1435,8 +1441,11 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp; return tmp;
mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) { while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS;
break;
}
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) { if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
...@@ -1467,12 +1476,16 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use ...@@ -1467,12 +1476,16 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
bytes -= tmp; bytes -= tmp;
xfer += tmp; xfer += tmp;
} }
}
mutex_unlock(&runtime->oss.params_lock);
return xfer;
err: err:
mutex_unlock(&runtime->oss.params_lock); mutex_unlock(&runtime->oss.params_lock);
if (tmp < 0)
break;
if (signal_pending(current)) {
tmp = -ERESTARTSYS;
break;
}
tmp = 0;
}
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
} }
......
...@@ -592,18 +592,26 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st ...@@ -592,18 +592,26 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
snd_pcm_sframes_t frames = size; snd_pcm_sframes_t frames = size;
plugin = snd_pcm_plug_first(plug); plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) { while (plugin) {
if (frames <= 0)
return frames;
if ((next = plugin->next) != NULL) { if ((next = plugin->next) != NULL) {
snd_pcm_sframes_t frames1 = frames; snd_pcm_sframes_t frames1 = frames;
if (plugin->dst_frames) if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames); frames1 = plugin->dst_frames(plugin, frames);
if (frames1 <= 0)
return frames1;
}
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
return err; return err;
} }
if (err != frames1) { if (err != frames1) {
frames = err; frames = err;
if (plugin->src_frames) if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1); frames = plugin->src_frames(plugin, frames1);
if (frames <= 0)
return frames;
}
} }
} else } else
dst_channels = NULL; dst_channels = NULL;
......
...@@ -1632,7 +1632,7 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, ...@@ -1632,7 +1632,7 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed; return changed;
if (params->rmask) { if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params); int err = snd_pcm_hw_refine(pcm, params);
if (snd_BUG_ON(err < 0)) if (err < 0)
return err; return err;
} }
return snd_pcm_hw_param_value(params, var, dir); return snd_pcm_hw_param_value(params, var, dir);
...@@ -1678,7 +1678,7 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, ...@@ -1678,7 +1678,7 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed; return changed;
if (params->rmask) { if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params); int err = snd_pcm_hw_refine(pcm, params);
if (snd_BUG_ON(err < 0)) if (err < 0)
return err; return err;
} }
return snd_pcm_hw_param_value(params, var, dir); return snd_pcm_hw_param_value(params, var, dir);
......
...@@ -2580,7 +2580,7 @@ static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, ...@@ -2580,7 +2580,7 @@ static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream,
return ret < 0 ? ret : frames; return ret < 0 ? ret : frames;
} }
/* decrease the appl_ptr; returns the processed frames or a negative error */ /* decrease the appl_ptr; returns the processed frames or zero for error */
static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
snd_pcm_uframes_t frames, snd_pcm_uframes_t frames,
snd_pcm_sframes_t avail) snd_pcm_sframes_t avail)
...@@ -2597,7 +2597,12 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, ...@@ -2597,7 +2597,12 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
if (appl_ptr < 0) if (appl_ptr < 0)
appl_ptr += runtime->boundary; appl_ptr += runtime->boundary;
ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
return ret < 0 ? ret : frames; /* NOTE: we return zero for errors because PulseAudio gets depressed
* upon receiving an error from rewind ioctl and stops processing
* any longer. Returning zero means that no rewind is done, so
* it's not absolutely wrong to answer like that.
*/
return ret < 0 ? 0 : frames;
} }
static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream,
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/initval.h> #include <sound/initval.h>
...@@ -305,19 +306,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -305,19 +306,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
return 0; return 0;
} }
static void params_change_substream(struct loopback_pcm *dpcm,
struct snd_pcm_runtime *runtime)
{
struct snd_pcm_runtime *dst_runtime;
if (dpcm == NULL || dpcm->substream == NULL)
return;
dst_runtime = dpcm->substream->runtime;
if (dst_runtime == NULL)
return;
dst_runtime->hw = dpcm->cable->hw;
}
static void params_change(struct snd_pcm_substream *substream) static void params_change(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
...@@ -329,10 +317,6 @@ static void params_change(struct snd_pcm_substream *substream) ...@@ -329,10 +317,6 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate; cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels; cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels; cable->hw.channels_max = runtime->channels;
params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
runtime);
params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
runtime);
} }
static int loopback_prepare(struct snd_pcm_substream *substream) static int loopback_prepare(struct snd_pcm_substream *substream)
...@@ -620,26 +604,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream) ...@@ -620,26 +604,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
static int rule_format(struct snd_pcm_hw_params *params, static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule) struct snd_pcm_hw_rule *rule)
{ {
struct loopback_pcm *dpcm = rule->private;
struct loopback_cable *cable = dpcm->cable;
struct snd_mask m;
struct snd_pcm_hardware *hw = rule->private; snd_mask_none(&m);
struct snd_mask *maskp = hw_param_mask(params, rule->var); mutex_lock(&dpcm->loopback->cable_lock);
m.bits[0] = (u_int32_t)cable->hw.formats;
maskp->bits[0] &= (u_int32_t)hw->formats; m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); mutex_unlock(&dpcm->loopback->cable_lock);
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
if (! maskp->bits[0] && ! maskp->bits[1])
return -EINVAL;
return 0;
} }
static int rule_rate(struct snd_pcm_hw_params *params, static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule) struct snd_pcm_hw_rule *rule)
{ {
struct snd_pcm_hardware *hw = rule->private; struct loopback_pcm *dpcm = rule->private;
struct loopback_cable *cable = dpcm->cable;
struct snd_interval t; struct snd_interval t;
t.min = hw->rate_min; mutex_lock(&dpcm->loopback->cable_lock);
t.max = hw->rate_max; t.min = cable->hw.rate_min;
t.max = cable->hw.rate_max;
mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0; t.openmin = t.openmax = 0;
t.integer = 0; t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t); return snd_interval_refine(hw_param_interval(params, rule->var), &t);
...@@ -648,22 +635,44 @@ static int rule_rate(struct snd_pcm_hw_params *params, ...@@ -648,22 +635,44 @@ static int rule_rate(struct snd_pcm_hw_params *params,
static int rule_channels(struct snd_pcm_hw_params *params, static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule) struct snd_pcm_hw_rule *rule)
{ {
struct snd_pcm_hardware *hw = rule->private; struct loopback_pcm *dpcm = rule->private;
struct loopback_cable *cable = dpcm->cable;
struct snd_interval t; struct snd_interval t;
t.min = hw->channels_min; mutex_lock(&dpcm->loopback->cable_lock);
t.max = hw->channels_max; t.min = cable->hw.channels_min;
t.max = cable->hw.channels_max;
mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0; t.openmin = t.openmax = 0;
t.integer = 0; t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t); return snd_interval_refine(hw_param_interval(params, rule->var), &t);
} }
static void free_cable(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
int dev = get_cable_index(substream);
struct loopback_cable *cable;
cable = loopback->cables[substream->number][dev];
if (!cable)
return;
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
cable->streams[substream->stream] = NULL;
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
kfree(cable);
}
}
static int loopback_open(struct snd_pcm_substream *substream) static int loopback_open(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data; struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm; struct loopback_pcm *dpcm;
struct loopback_cable *cable; struct loopback_cable *cable = NULL;
int err = 0; int err = 0;
int dev = get_cable_index(substream); int dev = get_cable_index(substream);
...@@ -681,7 +690,6 @@ static int loopback_open(struct snd_pcm_substream *substream) ...@@ -681,7 +690,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
if (!cable) { if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL); cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) { if (!cable) {
kfree(dpcm);
err = -ENOMEM; err = -ENOMEM;
goto unlock; goto unlock;
} }
...@@ -699,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream) ...@@ -699,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
/* are cached -> they do not reflect the actual state */ /* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0, err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_FORMAT,
rule_format, &runtime->hw, rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1); SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0) if (err < 0)
goto unlock; goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0, err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_RATE,
rule_rate, &runtime->hw, rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1); SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0) if (err < 0)
goto unlock; goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0, err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, SNDRV_PCM_HW_PARAM_CHANNELS,
rule_channels, &runtime->hw, rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1); SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0) if (err < 0)
goto unlock; goto unlock;
...@@ -723,6 +731,10 @@ static int loopback_open(struct snd_pcm_substream *substream) ...@@ -723,6 +731,10 @@ static int loopback_open(struct snd_pcm_substream *substream)
else else
runtime->hw = cable->hw; runtime->hw = cable->hw;
unlock: unlock:
if (err < 0) {
free_cable(substream);
kfree(dpcm);
}
mutex_unlock(&loopback->cable_lock); mutex_unlock(&loopback->cable_lock);
return err; return err;
} }
...@@ -731,20 +743,10 @@ static int loopback_close(struct snd_pcm_substream *substream) ...@@ -731,20 +743,10 @@ static int loopback_close(struct snd_pcm_substream *substream)
{ {
struct loopback *loopback = substream->private_data; struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data; struct loopback_pcm *dpcm = substream->runtime->private_data;
struct loopback_cable *cable;
int dev = get_cable_index(substream);
loopback_timer_stop(dpcm); loopback_timer_stop(dpcm);
mutex_lock(&loopback->cable_lock); mutex_lock(&loopback->cable_lock);
cable = loopback->cables[substream->number][dev]; free_cable(substream);
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
cable->streams[substream->stream] = NULL;
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
kfree(cable);
}
mutex_unlock(&loopback->cable_lock); mutex_unlock(&loopback->cable_lock);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment