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

Commit 974bfef3 authored by Ajay Dudani's avatar Ajay Dudani Committed by Ravi Kumar Alamanda
Browse files

ASoC: msm: Fix potential spin lock recursion in compress driver



1. compr_event_handler() in this driver is called from interrupt context. This
   function uses spin_lock_irq() and spin_unlock_irq().
2. Calling spin_unlock_irq() from interrupt handler will enable the interrupts
   and could result another interrupt while we are still in an interrupt
   handler.

Replace spin_lock_irq() with spin_lock() and spind_unlock_irq() with
spin_unlock() in the compr_event_handler() function.

Replace spin_lock_irq() with spin_lock_irqsave() and spin_unlock_irq() with
spin_unlock_irqrestore() in rest of file.

Change-Id: I19b98fe48ef6f66da4c2718cdeaba9df5878d2f5
Signed-off-by: default avatarNaveen Ramaraj <nramaraj@codeaurora.org>
Signed-off-by: default avatarAjay Dudani <adudani@codeaurora.org>
Signed-off-by: default avatarIliyan Malchev <malchev@google.com>
Git-commit: a43eebe1e74444b96275573cbec3f11253d40683
Git-repo: https://android.googlesource.com/kernel/msm


Signed-off-by: default avatarDhananjay Kumar <dhakumar@codeaurora.org>
parent 312d2603
Loading
Loading
Loading
Loading
+29 −25
Original line number Diff line number Diff line
@@ -183,7 +183,7 @@ static void compr_event_handler(uint32_t opcode,
	pr_debug("%s opcode =%08x\n", __func__, opcode);
	switch (opcode) {
	case ASM_DATA_EVENT_WRITE_DONE_V2:
		spin_lock_irq(&prtd->lock);
		spin_lock(&prtd->lock);

		if (payload[3]) {
			pr_err("WRITE FAILED w/ err 0x%x !, paddr 0x%x"
@@ -208,7 +208,7 @@ static void compr_event_handler(uint32_t opcode,
			/* Writes must be restarted from _copy() */
			pr_debug("write_done received while not started, treat as xrun");
			atomic_set(&prtd->xrun, 1);
			spin_unlock_irq(&prtd->lock);
			spin_unlock(&prtd->lock);
			break;
		}

@@ -226,7 +226,7 @@ static void compr_event_handler(uint32_t opcode,
		} else
			msm_compr_send_buffer(prtd);

		spin_unlock_irq(&prtd->lock);
		spin_unlock(&prtd->lock);
		break;
	case ASM_DATA_EVENT_RENDERED_EOS:
		pr_debug("ASM_DATA_CMDRSP_EOS\n");
@@ -254,8 +254,8 @@ static void compr_event_handler(uint32_t opcode,
			/* check if the first buffer need to be sent to DSP */
			pr_debug("ASM_SESSION_CMD_RUN_V2\n");

			spin_lock_irq(&prtd->lock);
			/* FIXME: A state is a better way, dealing with this*/
			spin_lock(&prtd->lock);
			if (!prtd->copied_total) {
				bytes_available = prtd->bytes_received - prtd->copied_total;
				if (bytes_available < cstream->runtime->fragment_size) {
@@ -264,7 +264,7 @@ static void compr_event_handler(uint32_t opcode,
				} else
					msm_compr_send_buffer(prtd);
			}
			spin_unlock_irq(&prtd->lock);
			spin_unlock(&prtd->lock);
			break;
		case ASM_STREAM_CMD_FLUSH:
			pr_debug("ASM_STREAM_CMD_FLUSH\n");
@@ -599,6 +599,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
	uint32_t *volume = pdata->volume[rtd->dai_link->be_id];
	int rc = 0;
	int bytes_to_write;
	unsigned long flags;

	if (cstream->direction != SND_COMPRESS_PLAYBACK) {
		pr_err("%s: Unsupported stream type\n", __func__);
@@ -618,7 +619,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
		spin_lock_irq(&prtd->lock);
		spin_lock_irqsave(&prtd->lock, flags);

		atomic_set(&prtd->start, 0);
		if (atomic_read(&prtd->eos)) {
@@ -633,7 +634,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)

		pr_debug("issue CMD_FLUSH\n");
		prtd->cmd_ack = 0;
		spin_unlock_irq(&prtd->lock);
		spin_unlock_irqrestore(&prtd->lock, flags);
		rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
		if (rc < 0) {
			pr_err("%s: flush cmd failed rc=%d\n",
@@ -648,14 +649,14 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		} else
			rc = 0; /* prtd->cmd_status == OK? 0 : -EPERM */

		spin_lock_irq(&prtd->lock);
		spin_lock_irqsave(&prtd->lock, flags);
		/* FIXME. only reset if flush was successful */
		prtd->byte_offset  = 0;
		prtd->copied_total = 0;
		prtd->app_pointer  = 0;
		prtd->bytes_received = 0;
		atomic_set(&prtd->xrun, 0);
		spin_unlock_irq(&prtd->lock);
		spin_unlock_irqrestore(&prtd->lock, flags);
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH\n");
@@ -672,13 +673,13 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
	case SND_COMPR_TRIGGER_DRAIN:
		pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__);
		/* Make sure all the data is sent to DSP before sending EOS */
		spin_lock_irq(&prtd->lock);
		spin_lock_irqsave(&prtd->lock, flags);

		if (!atomic_read(&prtd->start)) {
			pr_err("%s: stream is not in started state\n",
				__func__);
			rc = -EPERM;
			spin_unlock_irq(&prtd->lock);
			spin_unlock_irqrestore(&prtd->lock, flags);
			break;
		}
		atomic_set(&prtd->eos, 1);
@@ -686,7 +687,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		if (prtd->bytes_received > prtd->copied_total) {
			atomic_set(&prtd->drain, 1);
			prtd->drain_ready = 0;
			spin_unlock_irq(&prtd->lock);
			spin_unlock_irqrestore(&prtd->lock, flags);
			pr_debug("%s: wait till all the data is sent to dsp\n",
				__func__);

@@ -702,7 +703,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
							prtd->cmd_interrupt || prtd->drain_ready ||
							atomic_read(&prtd->xrun));

			spin_lock_irq(&prtd->lock);
			spin_lock_irqsave(&prtd->lock, flags);
			if (!prtd->cmd_interrupt) {
				bytes_to_write = prtd->bytes_received - prtd->copied_total;
				WARN(bytes_to_write > runtime->fragment_size,
@@ -722,7 +723,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
			pr_err("%s: stream is not started\n", __func__);
			rc = -EINTR;
			prtd->cmd_interrupt = 0;
			spin_unlock_irq(&prtd->lock);
			spin_unlock_irqrestore(&prtd->lock, flags);
			break;
		}

@@ -731,7 +732,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		prtd->cmd_ack = 0;
		q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);

		spin_unlock_irq(&prtd->lock);
		spin_unlock_irqrestore(&prtd->lock, flags);

/*
		if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) {
@@ -755,17 +756,17 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)

		/*FIXME : what if a flush comes while PC is here */
		if (rc == 0 && (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN)) {
			spin_lock_irq(&prtd->lock);
			spin_lock_irqsave(&prtd->lock, flags);
			pr_debug("%s: issue CMD_PAUSE ", __func__);
			q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
			prtd->cmd_ack = 0;
			spin_unlock_irq(&prtd->lock);
			spin_unlock_irqrestore(&prtd->lock, flags);
			pr_debug("%s: issue CMD_FLUSH", __func__);
			q6asm_cmd(prtd->audio_client, CMD_FLUSH);
			wait_event_timeout(prtd->flush_wait,
					   prtd->cmd_ack, 1 * HZ / 4);

			spin_lock_irq(&prtd->lock);
			spin_lock_irqsave(&prtd->lock, flags);
			prtd->byte_offset = 0;
			prtd->app_pointer  = 0;
			/* Don't reset these as these vars map
@@ -779,7 +780,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
			atomic_set(&prtd->xrun, 1);
			pr_debug("%s: issue CMD_RESUME", __func__);
			q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
			spin_unlock_irq(&prtd->lock);
			spin_unlock_irqrestore(&prtd->lock, flags);
		}
		pr_debug("%s: out of drain", __func__);

@@ -801,15 +802,16 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream,
	struct snd_compr_tstamp tstamp;
	uint64_t timestamp = 0;
	int rc = 0;
	unsigned long flags;

	pr_debug("%s\n", __func__);
	memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));

	spin_lock_irq(&prtd->lock);
	spin_lock_irqsave(&prtd->lock, flags);
	tstamp.sampling_rate = prtd->sample_rate;
	tstamp.byte_offset = prtd->byte_offset;
	tstamp.copied_total = prtd->copied_total;
	spin_unlock_irq(&prtd->lock);
	spin_unlock_irqrestore(&prtd->lock, flags);

	/*
	 Query timestamp from DSP if some data is with it.
@@ -840,6 +842,7 @@ static int msm_compr_ack(struct snd_compr_stream *cstream,
	struct msm_compr_audio *prtd = runtime->private_data;
	void *src, *dstn;
	size_t copy;
	unsigned long flags;

	WARN(1, "This path is untested");
	return -EINVAL;
@@ -866,7 +869,7 @@ static int msm_compr_ack(struct snd_compr_stream *cstream,
	 * copied to DSP, the newly received bytes should be
	 * sent right away
	 */
	spin_lock_irq(&prtd->lock);
	spin_lock_irqsave(&prtd->lock, flags);

	if (atomic_read(&prtd->start) &&
		prtd->bytes_received == prtd->copied_total) {
@@ -875,7 +878,7 @@ static int msm_compr_ack(struct snd_compr_stream *cstream,
	} else
		prtd->bytes_received += count;

	spin_unlock_irq(&prtd->lock);
	spin_unlock_irqrestore(&prtd->lock, flags);

	return 0;
}
@@ -888,6 +891,7 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
	void *dstn;
	size_t copy;
	size_t bytes_available = 0;
	unsigned long flags;

	pr_debug("%s: count = %d\n", __func__, count);
	if (!prtd->buffer) {
@@ -913,7 +917,7 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
	 * If stream is started and there has been an xrun,
	 * since the available bytes fits fragment_size, copy the data right away
	 */
	spin_lock_irq(&prtd->lock);
	spin_lock_irqsave(&prtd->lock, flags);

	prtd->bytes_received += count;
	if (atomic_read(&prtd->start)) {
@@ -930,7 +934,7 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
		} /* writes will continue on the next write_done */
	}

	spin_unlock_irq(&prtd->lock);
	spin_unlock_irqrestore(&prtd->lock, flags);

	return count;
}