sound fixes for 4.15-rc8
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 -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAlpUeWoOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE8rMxAAy+XJNWigvkWHd79ttKeAmndia/u9d+T6Ge/I VI/SJSy8vhnGO0YNf/AHEs6vtad73XnXP76x1H3TkCsrDxykfhKogCvp0Aat/Ji7 LQFkhQKsaEdACm2TlPxmxpO64sYB8UjvcZBFS82tCmNCldMkwi8T+DDDHocP0A0D pOQogjffqPBZdk7X1hJxoVKOm95GI1ms09+JPrLl47aa6mLIvNxa81RGnrVK5blE +kYZQAblweGN8RsMVWqyrnxgRatF59UbV6JIKui/8KD2AXl3Hya/Dn2aFWtMqqH8 p8siLsUI+tACPucNk7tMt9UjHEy7yGK02hClhYVZG6vZ81nSoJsJFTdwXBMKjrfy Fa1bBb8quM6WfBEHXB7YISulUrrc2nftkPhB/zIa5E9arkHWY4FL7jhdUTEAjkgr D0Ka3Q/PtdXxmK+NBqUpoiqDHoOQeA5HG+njsz5L0xSbxoxMy8guyxSaoeF4BOnW KbrVbzcJzSxDWPYGbKmeLEYHW8P3FOKNv9SI/WZErmyjQkeMiq7AuP93yYACFEyj LhSAxBZ00sStl6IgM4Unw6p4Gi0SOawQfADDG4Arfr/fRA52l9wmpaUwU3uJ1RMn gLvLfJkBbs/MwBjD5BPxfdjKIuREvMdUBwl/hZk1zp5d2ay0lAl6toNcee//MuHf DKd1t3I= =Fz+p -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
5f615b97cd
|
@ -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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user