mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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:
parent
9cd8115237
commit
3624f0bd4a
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user