From: Takashi Iwai <tiwai@suse.de>
To: linux-sound@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH] ALSA: line6: Handle period elapsed calls outside the locks
Date: Tue, 20 Oct 2026 13:31:17 +0200 [thread overview]
Message-ID: <20261020113118.234689-1-tiwai@suse.de> (raw)
LINE6 driver may call snd_pcm_period_elapsed() for both playback and
capture substreams at processing the playback substream, and this may
lead to a ABBA deadlock.
Move the actual calls of snd_pcm_period_elapsed() outside the locks
for avoiding it. The capture period notification at submitting the
initial playback URBs (only with the impulse mode) is simply skipped,
as it's done in the lock context; it'll be caught up at the next
period.
Fixes: f56742cc4189 ("ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/bug/linux-5a9de389-e6ea-410d-8322-e74e14152603
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
sound/usb/line6/capture.c | 25 ++++++++++++++++---------
sound/usb/line6/capture.h | 2 +-
sound/usb/line6/playback.c | 31 +++++++++++++++++++++----------
3 files changed, 38 insertions(+), 20 deletions(-)
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 3e1f40d6b481..17f49f567efb 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -126,24 +126,21 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
line6pcm->in.pos_done -= runtime->buffer_size;
}
-void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
+/* update the capture position and check whether we need period_elapsed call */
+bool line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
{
- struct snd_pcm_substream *substream =
- get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
-
line6pcm->in.bytes += length;
if (line6pcm->in.bytes >= line6pcm->in.period) {
line6pcm->in.bytes %= line6pcm->in.period;
- spin_unlock(&line6pcm->in.lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&line6pcm->in.lock);
+ return true;
}
+ return false;
}
/*
* Callback for completed capture URB.
*/
-static void audio_in_callback(struct urb *urb)
+static bool handle_audio_in(struct urb *urb)
{
int i, index, length = 0, shutdown = 0;
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
@@ -208,8 +205,18 @@ static void audio_in_callback(struct urb *urb)
if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
- line6_capture_check_period(line6pcm, length);
+ return line6_capture_check_period(line6pcm, length);
}
+
+ return false;
+}
+
+static void audio_in_callback(struct urb *urb)
+{
+ struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
+
+ if (handle_audio_in(urb))
+ snd_pcm_period_elapsed(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
}
/* open capture callback */
diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h
index 90572dae134e..628315389efc 100644
--- a/sound/usb/line6/capture.h
+++ b/sound/usb/line6/capture.h
@@ -17,7 +17,7 @@ extern const struct snd_pcm_ops snd_line6_capture_ops;
extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
int fsize);
-extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
+extern bool line6_capture_check_period(struct snd_line6_pcm *line6pcm,
int length);
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index aa6ddf8746a0..e93a16f6958e 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -136,6 +136,8 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
/*
Find a free URB, prepare audio data, and submit URB.
must be called in line6pcm->out.lock context
+ Returns 1 if snd_pcm_period_elapsed() needs to be called for the
+ capture stream after releasing the lock, 0 if not, or a negative error
*/
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{
@@ -151,6 +153,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
line6pcm->properties->rates.rats[0].den *
(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
struct urb *urb_out;
+ bool capture_period_elapsed = false;
index = find_first_zero_bit(&line6pcm->out.active_urbs,
line6pcm->line6->iso_buffers);
@@ -260,8 +263,9 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out->transfer_buffer,
urb_out->
transfer_buffer_length);
- line6_capture_check_period(line6pcm,
- urb_out->transfer_buffer_length);
+ capture_period_elapsed =
+ line6_capture_check_period(line6pcm,
+ urb_out->transfer_buffer_length);
}
} else {
if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
@@ -283,24 +287,26 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed (%d)\n", index, ret);
- return 0;
+ return capture_period_elapsed;
}
/*
Submit all currently available playback URBs.
must be called in line6pcm->out.lock context
+ The capture period notification is skipped here since we can't call
+ snd_pcm_period_elapsed() in the lock; it'll be caught up at the next one.
*/
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
- int ret = 0, i;
+ int ret, i;
for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
- break;
+ return ret;
}
- return ret;
+ return 0;
}
/*
@@ -316,6 +322,8 @@ static void audio_out_callback(struct urb *urb)
const int bytes_per_frame =
line6pcm->properties->bytes_per_channel *
line6pcm->properties->playback_hw.channels_max;
+ bool capture_period_elapsed = false;
+ bool playback_period_elapsed = false;
#if USE_CLEAR_BUFFER_WORKAROUND
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -358,19 +366,22 @@ static void audio_out_callback(struct urb *urb)
shutdown = 1;
if (!shutdown) {
- submit_audio_out_urb(line6pcm);
+ capture_period_elapsed = submit_audio_out_urb(line6pcm) > 0;
if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
line6pcm->out.bytes += length;
if (line6pcm->out.bytes >= line6pcm->out.period) {
line6pcm->out.bytes %= line6pcm->out.period;
- spin_unlock(&line6pcm->out.lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&line6pcm->out.lock);
+ playback_period_elapsed = true;
}
}
}
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
+
+ if (capture_period_elapsed)
+ snd_pcm_period_elapsed(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
+ if (playback_period_elapsed)
+ snd_pcm_period_elapsed(substream);
}
/* open playback callback */
--
2.55.0
reply other threads:[~2026-10-20 11:31 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261020113118.234689-1-tiwai@suse.de \
--to=tiwai@suse.de \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®