Skip to content

Commit dc74977

Browse files
committed
ALSA: seq: Fix concurrent access to queue current tick/time
snd_seq_check_queue() passes the current tick and time of the given queue as a pointer to snd_seq_prioq_cell_out(), but those might be updated concurrently by the seq timer update. Fix it by retrieving the current tick and time via the proper helper functions at first, and pass those values to snd_seq_prioq_cell_out() later in the loops. snd_seq_timer_get_cur_time() takes a new argument and adjusts with the current system time only when it's requested so; this update isn't needed for snd_seq_check_queue(), as it's called either from the interrupt handler or right after queuing. Also, snd_seq_timer_get_cur_tick() is changed to read the value in the spinlock for the concurrency, too. Reported-by: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent bb51e66 commit dc74977

File tree

4 files changed

+20
-9
lines changed

4 files changed

+20
-9
lines changed

sound/core/seq/seq_clientmgr.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
580580
event->queue = queue;
581581
event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
582582
if (real_time) {
583-
event->time.time = snd_seq_timer_get_cur_time(q->timer);
583+
event->time.time = snd_seq_timer_get_cur_time(q->timer, true);
584584
event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
585585
} else {
586586
event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
@@ -1659,7 +1659,7 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
16591659
tmr = queue->timer;
16601660
status->events = queue->tickq->cells + queue->timeq->cells;
16611661

1662-
status->time = snd_seq_timer_get_cur_time(tmr);
1662+
status->time = snd_seq_timer_get_cur_time(tmr, true);
16631663
status->tick = snd_seq_timer_get_cur_tick(tmr);
16641664

16651665
status->running = tmr->running;

sound/core/seq/seq_queue.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
238238
{
239239
unsigned long flags;
240240
struct snd_seq_event_cell *cell;
241+
snd_seq_tick_time_t cur_tick;
242+
snd_seq_real_time_t cur_time;
241243

242244
if (q == NULL)
243245
return;
@@ -254,17 +256,18 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
254256

255257
__again:
256258
/* Process tick queue... */
259+
cur_tick = snd_seq_timer_get_cur_tick(q->timer);
257260
for (;;) {
258-
cell = snd_seq_prioq_cell_out(q->tickq,
259-
&q->timer->tick.cur_tick);
261+
cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick);
260262
if (!cell)
261263
break;
262264
snd_seq_dispatch_event(cell, atomic, hop);
263265
}
264266

265267
/* Process time queue... */
268+
cur_time = snd_seq_timer_get_cur_time(q->timer, false);
266269
for (;;) {
267-
cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time);
270+
cell = snd_seq_prioq_cell_out(q->timeq, &cur_time);
268271
if (!cell)
269272
break;
270273
snd_seq_dispatch_event(cell, atomic, hop);

sound/core/seq/seq_timer.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,14 +428,15 @@ int snd_seq_timer_continue(struct snd_seq_timer *tmr)
428428
}
429429

430430
/* return current 'real' time. use timeofday() to get better granularity. */
431-
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
431+
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
432+
bool adjust_ktime)
432433
{
433434
snd_seq_real_time_t cur_time;
434435
unsigned long flags;
435436

436437
spin_lock_irqsave(&tmr->lock, flags);
437438
cur_time = tmr->cur_time;
438-
if (tmr->running) {
439+
if (adjust_ktime && tmr->running) {
439440
struct timespec64 tm;
440441

441442
ktime_get_ts64(&tm);
@@ -452,7 +453,13 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
452453
high PPQ values) */
453454
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
454455
{
455-
return tmr->tick.cur_tick;
456+
snd_seq_tick_time_t cur_tick;
457+
unsigned long flags;
458+
459+
spin_lock_irqsave(&tmr->lock, flags);
460+
cur_tick = tmr->tick.cur_tick;
461+
spin_unlock_irqrestore(&tmr->lock, flags);
462+
return cur_tick;
456463
}
457464

458465

sound/core/seq/seq_timer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);
120120
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
121121
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
122122
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
123-
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
123+
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
124+
bool adjust_ktime);
124125
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
125126

126127
extern int seq_default_timer_class;

0 commit comments

Comments
 (0)