Skip to content

Commit bea8a67

Browse files
tiwaigregkh
authored andcommitted
ALSA: timer: Call notifier in the same spinlock
commit f65e0d2 upstream. snd_timer_notify1() is called outside the spinlock and it retakes the lock after the unlock. This is rather racy, and it's safer to move snd_timer_notify() call inside the main spinlock. The patch also contains a slight refactoring / cleanup of the code. Now all start/stop/continue/pause look more symmetric and a bit better readable. Signed-off-by: Takashi Iwai <[email protected]> Cc: Ben Hutchings <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 957adaa commit bea8a67

File tree

1 file changed

+102
-118
lines changed

1 file changed

+102
-118
lines changed

sound/core/timer.c

Lines changed: 102 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
318318
return 0;
319319
}
320320

321-
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
322-
323321
/*
324322
* close a timer instance
325323
*/
@@ -408,7 +406,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
408406
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
409407
{
410408
struct snd_timer *timer;
411-
unsigned long flags;
412409
unsigned long resolution = 0;
413410
struct snd_timer_instance *ts;
414411
struct timespec tstamp;
@@ -432,34 +429,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
432429
return;
433430
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
434431
return;
435-
spin_lock_irqsave(&timer->lock, flags);
436432
list_for_each_entry(ts, &ti->slave_active_head, active_list)
437433
if (ts->ccallback)
438434
ts->ccallback(ts, event + 100, &tstamp, resolution);
439-
spin_unlock_irqrestore(&timer->lock, flags);
440435
}
441436

442-
static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
443-
unsigned long sticks)
437+
/* start/continue a master timer */
438+
static int snd_timer_start1(struct snd_timer_instance *timeri,
439+
bool start, unsigned long ticks)
444440
{
441+
struct snd_timer *timer;
442+
int result;
443+
unsigned long flags;
444+
445+
timer = timeri->timer;
446+
if (!timer)
447+
return -EINVAL;
448+
449+
spin_lock_irqsave(&timer->lock, flags);
450+
if (timer->card && timer->card->shutdown) {
451+
result = -ENODEV;
452+
goto unlock;
453+
}
454+
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
455+
SNDRV_TIMER_IFLG_START)) {
456+
result = -EBUSY;
457+
goto unlock;
458+
}
459+
460+
if (start)
461+
timeri->ticks = timeri->cticks = ticks;
462+
else if (!timeri->cticks)
463+
timeri->cticks = 1;
464+
timeri->pticks = 0;
465+
445466
list_move_tail(&timeri->active_list, &timer->active_list_head);
446467
if (timer->running) {
447468
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
448469
goto __start_now;
449470
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
450471
timeri->flags |= SNDRV_TIMER_IFLG_START;
451-
return 1; /* delayed start */
472+
result = 1; /* delayed start */
452473
} else {
453-
timer->sticks = sticks;
474+
if (start)
475+
timer->sticks = ticks;
454476
timer->hw.start(timer);
455477
__start_now:
456478
timer->running++;
457479
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
458-
return 0;
480+
result = 0;
459481
}
482+
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
483+
SNDRV_TIMER_EVENT_CONTINUE);
484+
unlock:
485+
spin_unlock_irqrestore(&timer->lock, flags);
486+
return result;
460487
}
461488

462-
static int snd_timer_start_slave(struct snd_timer_instance *timeri)
489+
/* start/continue a slave timer */
490+
static int snd_timer_start_slave(struct snd_timer_instance *timeri,
491+
bool start)
463492
{
464493
unsigned long flags;
465494

@@ -473,88 +502,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
473502
spin_lock(&timeri->timer->lock);
474503
list_add_tail(&timeri->active_list,
475504
&timeri->master->slave_active_head);
505+
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
506+
SNDRV_TIMER_EVENT_CONTINUE);
476507
spin_unlock(&timeri->timer->lock);
477508
}
478509
spin_unlock_irqrestore(&slave_active_lock, flags);
479510
return 1; /* delayed start */
480511
}
481512

482-
/*
483-
* start the timer instance
484-
*/
485-
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
513+
/* stop/pause a master timer */
514+
static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
486515
{
487516
struct snd_timer *timer;
488-
int result = -EINVAL;
517+
int result = 0;
489518
unsigned long flags;
490519

491-
if (timeri == NULL || ticks < 1)
492-
return -EINVAL;
493-
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
494-
result = snd_timer_start_slave(timeri);
495-
if (result >= 0)
496-
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
497-
return result;
498-
}
499-
timer = timeri->timer;
500-
if (timer == NULL)
501-
return -EINVAL;
502-
if (timer->card && timer->card->shutdown)
503-
return -ENODEV;
504-
spin_lock_irqsave(&timer->lock, flags);
505-
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
506-
SNDRV_TIMER_IFLG_START)) {
507-
result = -EBUSY;
508-
goto unlock;
509-
}
510-
timeri->ticks = timeri->cticks = ticks;
511-
timeri->pticks = 0;
512-
result = snd_timer_start1(timer, timeri, ticks);
513-
unlock:
514-
spin_unlock_irqrestore(&timer->lock, flags);
515-
if (result >= 0)
516-
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
517-
return result;
518-
}
519-
520-
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
521-
{
522-
struct snd_timer *timer;
523-
unsigned long flags;
524-
525-
if (snd_BUG_ON(!timeri))
526-
return -ENXIO;
527-
528-
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
529-
spin_lock_irqsave(&slave_active_lock, flags);
530-
if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
531-
spin_unlock_irqrestore(&slave_active_lock, flags);
532-
return -EBUSY;
533-
}
534-
if (timeri->timer)
535-
spin_lock(&timeri->timer->lock);
536-
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
537-
list_del_init(&timeri->ack_list);
538-
list_del_init(&timeri->active_list);
539-
if (timeri->timer)
540-
spin_unlock(&timeri->timer->lock);
541-
spin_unlock_irqrestore(&slave_active_lock, flags);
542-
goto __end;
543-
}
544520
timer = timeri->timer;
545521
if (!timer)
546522
return -EINVAL;
547523
spin_lock_irqsave(&timer->lock, flags);
548524
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
549525
SNDRV_TIMER_IFLG_START))) {
550-
spin_unlock_irqrestore(&timer->lock, flags);
551-
return -EBUSY;
526+
result = -EBUSY;
527+
goto unlock;
552528
}
553529
list_del_init(&timeri->ack_list);
554530
list_del_init(&timeri->active_list);
555-
if (timer->card && timer->card->shutdown) {
556-
spin_unlock_irqrestore(&timer->lock, flags);
557-
return 0;
531+
if (timer->card && timer->card->shutdown)
532+
goto unlock;
533+
if (stop) {
534+
timeri->cticks = timeri->ticks;
535+
timeri->pticks = 0;
558536
}
559537
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
560538
!(--timer->running)) {
@@ -569,76 +547,82 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
569547
}
570548
}
571549
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
550+
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
551+
SNDRV_TIMER_EVENT_CONTINUE);
552+
unlock:
572553
spin_unlock_irqrestore(&timer->lock, flags);
573-
__end:
574-
if (event != SNDRV_TIMER_EVENT_RESOLUTION)
575-
snd_timer_notify1(timeri, event);
554+
return result;
555+
}
556+
557+
/* stop/pause a slave timer */
558+
static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
559+
{
560+
unsigned long flags;
561+
562+
spin_lock_irqsave(&slave_active_lock, flags);
563+
if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
564+
spin_unlock_irqrestore(&slave_active_lock, flags);
565+
return -EBUSY;
566+
}
567+
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
568+
if (timeri->timer) {
569+
spin_lock(&timeri->timer->lock);
570+
list_del_init(&timeri->ack_list);
571+
list_del_init(&timeri->active_list);
572+
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
573+
SNDRV_TIMER_EVENT_CONTINUE);
574+
spin_unlock(&timeri->timer->lock);
575+
}
576+
spin_unlock_irqrestore(&slave_active_lock, flags);
576577
return 0;
577578
}
578579

580+
/*
581+
* start the timer instance
582+
*/
583+
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
584+
{
585+
if (timeri == NULL || ticks < 1)
586+
return -EINVAL;
587+
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
588+
return snd_timer_start_slave(timeri, true);
589+
else
590+
return snd_timer_start1(timeri, true, ticks);
591+
}
592+
579593
/*
580594
* stop the timer instance.
581595
*
582596
* do not call this from the timer callback!
583597
*/
584598
int snd_timer_stop(struct snd_timer_instance *timeri)
585599
{
586-
struct snd_timer *timer;
587-
unsigned long flags;
588-
int err;
589-
590-
err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
591-
if (err < 0)
592-
return err;
593-
timer = timeri->timer;
594-
if (!timer)
595-
return -EINVAL;
596-
spin_lock_irqsave(&timer->lock, flags);
597-
timeri->cticks = timeri->ticks;
598-
timeri->pticks = 0;
599-
spin_unlock_irqrestore(&timer->lock, flags);
600-
return 0;
600+
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
601+
return snd_timer_stop_slave(timeri, true);
602+
else
603+
return snd_timer_stop1(timeri, true);
601604
}
602605

603606
/*
604607
* start again.. the tick is kept.
605608
*/
606609
int snd_timer_continue(struct snd_timer_instance *timeri)
607610
{
608-
struct snd_timer *timer;
609-
int result = -EINVAL;
610-
unsigned long flags;
611-
612-
if (timeri == NULL)
613-
return result;
614611
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
615-
return snd_timer_start_slave(timeri);
616-
timer = timeri->timer;
617-
if (! timer)
618-
return -EINVAL;
619-
if (timer->card && timer->card->shutdown)
620-
return -ENODEV;
621-
spin_lock_irqsave(&timer->lock, flags);
622-
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
623-
result = -EBUSY;
624-
goto unlock;
625-
}
626-
if (!timeri->cticks)
627-
timeri->cticks = 1;
628-
timeri->pticks = 0;
629-
result = snd_timer_start1(timer, timeri, timer->sticks);
630-
unlock:
631-
spin_unlock_irqrestore(&timer->lock, flags);
632-
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
633-
return result;
612+
return snd_timer_start_slave(timeri, false);
613+
else
614+
return snd_timer_start1(timeri, false, 0);
634615
}
635616

636617
/*
637618
* pause.. remember the ticks left
638619
*/
639620
int snd_timer_pause(struct snd_timer_instance * timeri)
640621
{
641-
return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
622+
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
623+
return snd_timer_stop_slave(timeri, false);
624+
else
625+
return snd_timer_stop1(timeri, false);
642626
}
643627

644628
/*

0 commit comments

Comments
 (0)