aboutsummaryrefslogtreecommitdiffstats
path: root/sound/drivers
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-10-21 04:53:14 -0400
committerTakashi Iwai <tiwai@suse.de>2012-10-21 04:53:14 -0400
commit999fc9f6aa37bac18f82d5ed25fa920a0dd93218 (patch)
treed89070175331a427142d15da6a0d2266e9f2e69f /sound/drivers
parent950f40fdd4732e5161e244812cbd9557c4e1d86c (diff)
ALSA: aloop - Close races at restarting the stream
There are small races opened in the check of running bit and the timer lock. Instead of adding yet more flag, just protect the whole racy codes with the existing cable->lock. As a bonus, we can get rid of timer_lock now. Reported-and-tested-by: Omair Mohammed Abdullah <omair.m.abdullah@linux.intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/drivers')
-rw-r--r--sound/drivers/aloop.c36
1 files changed, 19 insertions, 17 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 0fe6d64ff840..1904046686e2 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -120,7 +120,6 @@ struct loopback_pcm {
120 unsigned int last_drift; 120 unsigned int last_drift;
121 unsigned long last_jiffies; 121 unsigned long last_jiffies;
122 struct timer_list timer; 122 struct timer_list timer;
123 spinlock_t timer_lock;
124}; 123};
125 124
126static struct platform_device *devices[SNDRV_CARDS]; 125static struct platform_device *devices[SNDRV_CARDS];
@@ -166,12 +165,12 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
166 return get_setup(dpcm)->rate_shift; 165 return get_setup(dpcm)->rate_shift;
167} 166}
168 167
168/* call in cable->lock */
169static void loopback_timer_start(struct loopback_pcm *dpcm) 169static void loopback_timer_start(struct loopback_pcm *dpcm)
170{ 170{
171 unsigned long tick; 171 unsigned long tick;
172 unsigned int rate_shift = get_rate_shift(dpcm); 172 unsigned int rate_shift = get_rate_shift(dpcm);
173 173
174 spin_lock(&dpcm->timer_lock);
175 if (rate_shift != dpcm->pcm_rate_shift) { 174 if (rate_shift != dpcm->pcm_rate_shift) {
176 dpcm->pcm_rate_shift = rate_shift; 175 dpcm->pcm_rate_shift = rate_shift;
177 dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); 176 dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
@@ -184,15 +183,13 @@ static void loopback_timer_start(struct loopback_pcm *dpcm)
184 tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; 183 tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
185 dpcm->timer.expires = jiffies + tick; 184 dpcm->timer.expires = jiffies + tick;
186 add_timer(&dpcm->timer); 185 add_timer(&dpcm->timer);
187 spin_unlock(&dpcm->timer_lock);
188} 186}
189 187
188/* call in cable->lock */
190static inline void loopback_timer_stop(struct loopback_pcm *dpcm) 189static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
191{ 190{
192 spin_lock(&dpcm->timer_lock);
193 del_timer(&dpcm->timer); 191 del_timer(&dpcm->timer);
194 dpcm->timer.expires = 0; 192 dpcm->timer.expires = 0;
195 spin_unlock(&dpcm->timer_lock);
196} 193}
197 194
198#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) 195#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
@@ -274,8 +271,8 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
274 spin_lock(&cable->lock); 271 spin_lock(&cable->lock);
275 cable->running |= stream; 272 cable->running |= stream;
276 cable->pause &= ~stream; 273 cable->pause &= ~stream;
277 spin_unlock(&cable->lock);
278 loopback_timer_start(dpcm); 274 loopback_timer_start(dpcm);
275 spin_unlock(&cable->lock);
279 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 276 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
280 loopback_active_notify(dpcm); 277 loopback_active_notify(dpcm);
281 break; 278 break;
@@ -283,23 +280,23 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
283 spin_lock(&cable->lock); 280 spin_lock(&cable->lock);
284 cable->running &= ~stream; 281 cable->running &= ~stream;
285 cable->pause &= ~stream; 282 cable->pause &= ~stream;
286 spin_unlock(&cable->lock);
287 loopback_timer_stop(dpcm); 283 loopback_timer_stop(dpcm);
284 spin_unlock(&cable->lock);
288 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 285 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
289 loopback_active_notify(dpcm); 286 loopback_active_notify(dpcm);
290 break; 287 break;
291 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 288 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
292 spin_lock(&cable->lock); 289 spin_lock(&cable->lock);
293 cable->pause |= stream; 290 cable->pause |= stream;
294 spin_unlock(&cable->lock);
295 loopback_timer_stop(dpcm); 291 loopback_timer_stop(dpcm);
292 spin_unlock(&cable->lock);
296 break; 293 break;
297 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 294 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
298 spin_lock(&cable->lock); 295 spin_lock(&cable->lock);
299 dpcm->last_jiffies = jiffies; 296 dpcm->last_jiffies = jiffies;
300 cable->pause &= ~stream; 297 cable->pause &= ~stream;
301 spin_unlock(&cable->lock);
302 loopback_timer_start(dpcm); 298 loopback_timer_start(dpcm);
299 spin_unlock(&cable->lock);
303 break; 300 break;
304 default: 301 default:
305 return -EINVAL; 302 return -EINVAL;
@@ -477,6 +474,7 @@ static inline void bytepos_finish(struct loopback_pcm *dpcm,
477 dpcm->buf_pos %= dpcm->pcm_buffer_size; 474 dpcm->buf_pos %= dpcm->pcm_buffer_size;
478} 475}
479 476
477/* call in cable->lock */
480static unsigned int loopback_pos_update(struct loopback_cable *cable) 478static unsigned int loopback_pos_update(struct loopback_cable *cable)
481{ 479{
482 struct loopback_pcm *dpcm_play = 480 struct loopback_pcm *dpcm_play =
@@ -485,9 +483,7 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
485 cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 483 cable->streams[SNDRV_PCM_STREAM_CAPTURE];
486 unsigned long delta_play = 0, delta_capt = 0; 484 unsigned long delta_play = 0, delta_capt = 0;
487 unsigned int running, count1, count2; 485 unsigned int running, count1, count2;
488 unsigned long flags;
489 486
490 spin_lock_irqsave(&cable->lock, flags);
491 running = cable->running ^ cable->pause; 487 running = cable->running ^ cable->pause;
492 if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { 488 if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
493 delta_play = jiffies - dpcm_play->last_jiffies; 489 delta_play = jiffies - dpcm_play->last_jiffies;
@@ -529,32 +525,39 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
529 bytepos_finish(dpcm_play, count1); 525 bytepos_finish(dpcm_play, count1);
530 bytepos_finish(dpcm_capt, count1); 526 bytepos_finish(dpcm_capt, count1);
531 unlock: 527 unlock:
532 spin_unlock_irqrestore(&cable->lock, flags);
533 return running; 528 return running;
534} 529}
535 530
536static void loopback_timer_function(unsigned long data) 531static void loopback_timer_function(unsigned long data)
537{ 532{
538 struct loopback_pcm *dpcm = (struct loopback_pcm *)data; 533 struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
539 unsigned int running; 534 unsigned long flags;
540 535
541 running = loopback_pos_update(dpcm->cable); 536 spin_lock_irqsave(&dpcm->cable->lock, flags);
542 if (running & (1 << dpcm->substream->stream)) { 537 if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
543 loopback_timer_start(dpcm); 538 loopback_timer_start(dpcm);
544 if (dpcm->period_update_pending) { 539 if (dpcm->period_update_pending) {
545 dpcm->period_update_pending = 0; 540 dpcm->period_update_pending = 0;
541 spin_unlock_irqrestore(&dpcm->cable->lock, flags);
542 /* need to unlock before calling below */
546 snd_pcm_period_elapsed(dpcm->substream); 543 snd_pcm_period_elapsed(dpcm->substream);
544 return;
547 } 545 }
548 } 546 }
547 spin_unlock_irqrestore(&dpcm->cable->lock, flags);
549} 548}
550 549
551static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) 550static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
552{ 551{
553 struct snd_pcm_runtime *runtime = substream->runtime; 552 struct snd_pcm_runtime *runtime = substream->runtime;
554 struct loopback_pcm *dpcm = runtime->private_data; 553 struct loopback_pcm *dpcm = runtime->private_data;
554 snd_pcm_uframes_t pos;
555 555
556 spin_lock(&dpcm->cable->lock);
556 loopback_pos_update(dpcm->cable); 557 loopback_pos_update(dpcm->cable);
557 return bytes_to_frames(runtime, dpcm->buf_pos); 558 pos = dpcm->buf_pos;
559 spin_unlock(&dpcm->cable->lock);
560 return bytes_to_frames(runtime, pos);
558} 561}
559 562
560static struct snd_pcm_hardware loopback_pcm_hardware = 563static struct snd_pcm_hardware loopback_pcm_hardware =
@@ -672,7 +675,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
672 dpcm->substream = substream; 675 dpcm->substream = substream;
673 setup_timer(&dpcm->timer, loopback_timer_function, 676 setup_timer(&dpcm->timer, loopback_timer_function,
674 (unsigned long)dpcm); 677 (unsigned long)dpcm);
675 spin_lock_init(&dpcm->timer_lock);
676 678
677 cable = loopback->cables[substream->number][dev]; 679 cable = loopback->cables[substream->number][dev];
678 if (!cable) { 680 if (!cable) {