ALSA: xen-front: Connect event channel after stream prepare

The request channel must be connected from ALSA .open(), because hw-rule
queries and the stream open request use it. The event channel is
different: XENSND_EVT_CUR_POS handling uses ALSA runtime buffer and
period geometry, and the corresponding Xen stream parameters are not
submitted to the backend until .prepare() sends XENSND_OP_OPEN.

Currently .open() connects both channels. A backend current-position
event, or a stale event queued for an earlier stream instance, can
therefore reach xen_snd_front_alsa_handle_cur_pos() before
runtime->buffer_size and runtime->period_size are valid.

Add a per-channel connection helper, connect only the request channel in
.open(), connect the event channel after a successful stream prepare,
and disconnect it before stream close/free. Re-check the event-channel
state after taking ring_io_lock so disconnecting the event channel
synchronizes against a threaded IRQ that passed the initial lockless
state test. Keep defensive runtime geometry checks in the position
handler.

Fixes: 1cee559351 ("ALSA: xen-front: Implement ALSA virtual sound driver")
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260526-alsa-xen-event-channel-fixes-v1-2-91d3a6a50778@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Cássio Gabriel 2026-05-26 09:48:27 -03:00 committed by Takashi Iwai
parent 9cd8115237
commit 3624f0bd4a
3 changed files with 27 additions and 12 deletions

View File

@ -378,7 +378,7 @@ static int alsa_open(struct snd_pcm_substream *substream)
stream_clear(stream);
xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->req, true);
ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
alsa_hw_rule, stream,
@ -498,6 +498,8 @@ static int alsa_hw_free(struct snd_pcm_substream *substream)
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
int ret;
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, false);
ret = xen_snd_front_stream_close(&stream->evt_pair->req);
stream_free(stream);
return ret;
@ -532,6 +534,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream)
return ret;
stream->is_open = true;
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, true);
}
return 0;
@ -571,20 +574,24 @@ void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
{
struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
if (!runtime->buffer_size || !runtime->period_size)
return;
cur_frame = bytes_to_frames(runtime, pos_bytes);
delta = cur_frame - stream->be_cur_frame;
stream->be_cur_frame = cur_frame;
new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
new_hw_ptr = (new_hw_ptr + delta) % runtime->buffer_size;
atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
stream->out_frames += delta;
if (stream->out_frames > substream->runtime->period_size) {
stream->out_frames %= substream->runtime->period_size;
if (stream->out_frames > runtime->period_size) {
stream->out_frames %= runtime->period_size;
snd_pcm_period_elapsed(substream);
}
}

View File

@ -94,6 +94,9 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
guard(mutex)(&channel->ring_io_lock);
if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
return IRQ_HANDLED;
prod = page->in_prod;
/* Ensure we see ring contents up to prod. */
virt_rmb();
@ -430,8 +433,8 @@ int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info)
return ret;
}
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected)
void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel,
bool is_connected)
{
enum xen_snd_front_evtchnl_state state;
@ -440,13 +443,16 @@ void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair
else
state = EVTCHNL_STATE_DISCONNECTED;
scoped_guard(mutex, &evt_pair->req.ring_io_lock) {
evt_pair->req.state = state;
scoped_guard(mutex, &channel->ring_io_lock) {
channel->state = state;
}
}
scoped_guard(mutex, &evt_pair->evt.ring_io_lock) {
evt_pair->evt.state = state;
}
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected)
{
xen_snd_front_evtchnl_set_connected(&evt_pair->req, is_connected);
xen_snd_front_evtchnl_set_connected(&evt_pair->evt, is_connected);
}
void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair)

View File

@ -77,6 +77,8 @@ void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info);
int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info);
void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl);
void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel,
bool is_connected);
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected);