Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f615b97 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+27 −14
Original line number Diff line number Diff line
@@ -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;
}

+11 −3
Original line number Diff line number Diff line
@@ -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;
+2 −2
Original line number Diff line number Diff line
@@ -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);
+7 −2
Original line number Diff line number Diff line
@@ -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,
+50 −48
Original line number Diff line number Diff line
@@ -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;
}