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,
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
snd_BUG_ON(v < 0);
return v;
}
......@@ -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)
return tmp;
mutex_lock(&runtime->oss.params_lock);
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) {
tmp = 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
xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
break;
}
tmp = -EAGAIN;
}
mutex_unlock(&runtime->oss.params_lock);
return xfer;
err:
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;
}
......@@ -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)
return tmp;
mutex_lock(&runtime->oss.params_lock);
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 (runtime->oss.buffer_used == 0) {
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
bytes -= tmp;
xfer += tmp;
}
}
mutex_unlock(&runtime->oss.params_lock);
return xfer;
err:
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;
}
......
......@@ -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;
plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) {
while (plugin) {
if (frames <= 0)
return frames;
if ((next = plugin->next) != NULL) {
snd_pcm_sframes_t frames1 = frames;
if (plugin->dst_frames)
if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
if (frames1 <= 0)
return frames1;
}
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
return err;
}
if (err != frames1) {
frames = err;
if (plugin->src_frames)
if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1);
if (frames <= 0)
return frames;
}
}
} else
dst_channels = NULL;
......
......@@ -1632,7 +1632,7 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
if (snd_BUG_ON(err < 0))
if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
......@@ -1678,7 +1678,7 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
if (snd_BUG_ON(err < 0))
if (err < 0)
return err;
}
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,
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,
snd_pcm_uframes_t frames,
snd_pcm_sframes_t avail)
......@@ -2597,7 +2597,12 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
if (appl_ptr < 0)
appl_ptr += runtime->boundary;
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,
......
......@@ -39,6 +39,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
......@@ -305,19 +306,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
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)
{
struct snd_pcm_runtime *runtime = substream->runtime;
......@@ -329,10 +317,6 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = 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)
......@@ -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,
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;
struct snd_mask *maskp = hw_param_mask(params, rule->var);
maskp->bits[0] &= (u_int32_t)hw->formats;
maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
if (! maskp->bits[0] && ! maskp->bits[1])
return -EINVAL;
return 0;
snd_mask_none(&m);
mutex_lock(&dpcm->loopback->cable_lock);
m.bits[0] = (u_int32_t)cable->hw.formats;
m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
mutex_unlock(&dpcm->loopback->cable_lock);
return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
static int rule_rate(struct snd_pcm_hw_params *params,
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;
t.min = hw->rate_min;
t.max = hw->rate_max;
mutex_lock(&dpcm->loopback->cable_lock);
t.min = cable->hw.rate_min;
t.max = cable->hw.rate_max;
mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
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,
static int rule_channels(struct snd_pcm_hw_params *params,
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;
t.min = hw->channels_min;
t.max = hw->channels_max;
mutex_lock(&dpcm->loopback->cable_lock);
t.min = cable->hw.channels_min;
t.max = cable->hw.channels_max;
mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
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)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm;
struct loopback_cable *cable;
struct loopback_cable *cable = NULL;
int err = 0;
int dev = get_cable_index(substream);
......@@ -681,7 +690,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) {
kfree(dpcm);
err = -ENOMEM;
goto unlock;
}
......@@ -699,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
/* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
rule_format, &runtime->hw,
rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
rule_rate, &runtime->hw,
rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
rule_channels, &runtime->hw,
rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto unlock;
......@@ -723,6 +731,10 @@ static int loopback_open(struct snd_pcm_substream *substream)
else
runtime->hw = cable->hw;
unlock:
if (err < 0) {
free_cable(substream);
kfree(dpcm);
}
mutex_unlock(&loopback->cable_lock);
return err;
}
......@@ -731,20 +743,10 @@ static int loopback_close(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
struct loopback_cable *cable;
int dev = get_cable_index(substream);
loopback_timer_stop(dpcm);
mutex_lock(&loopback->cable_lock);
cable = loopback->cables[substream->number][dev];
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);
}
free_cable(substream);
mutex_unlock(&loopback->cable_lock);
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