diff options
author | Omair Mohammed Abdullah <omair.m.abdullah@linux.intel.com> | 2012-09-29 02:54:05 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-10-06 10:50:28 -0400 |
commit | d4f1e48bd11e3df6a26811f7a1f06c4225d92f7d (patch) | |
tree | 9095cf6f07b86a43df416c7ebdd261967935053d /sound/drivers | |
parent | d17344b3547669f5b6ee4fda993d03737a141bd6 (diff) |
ALSA: aloop - add locking to timer access
When the loopback timer handler is running, calling del_timer() (for STOP
trigger) will not wait for the handler to complete before deactivating the
timer. The timer gets rescheduled in the handler as usual. Then a subsequent
START trigger will try to start the timer using add_timer() with a timer pending
leading to a kernel panic.
Serialize the calls to add_timer() and del_timer() using a spin lock to avoid
this.
Signed-off-by: Omair Mohammed Abdullah <omair.m.abdullah@linux.intel.com>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/drivers')
-rw-r--r-- | sound/drivers/aloop.c | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 5a34355e78e8..0fe6d64ff840 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
@@ -120,6 +120,7 @@ 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; | ||
123 | }; | 124 | }; |
124 | 125 | ||
125 | static struct platform_device *devices[SNDRV_CARDS]; | 126 | static struct platform_device *devices[SNDRV_CARDS]; |
@@ -170,6 +171,7 @@ static void loopback_timer_start(struct loopback_pcm *dpcm) | |||
170 | unsigned long tick; | 171 | unsigned long tick; |
171 | unsigned int rate_shift = get_rate_shift(dpcm); | 172 | unsigned int rate_shift = get_rate_shift(dpcm); |
172 | 173 | ||
174 | spin_lock(&dpcm->timer_lock); | ||
173 | if (rate_shift != dpcm->pcm_rate_shift) { | 175 | if (rate_shift != dpcm->pcm_rate_shift) { |
174 | dpcm->pcm_rate_shift = rate_shift; | 176 | dpcm->pcm_rate_shift = rate_shift; |
175 | dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); | 177 | dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); |
@@ -182,12 +184,15 @@ static void loopback_timer_start(struct loopback_pcm *dpcm) | |||
182 | tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; | 184 | tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; |
183 | dpcm->timer.expires = jiffies + tick; | 185 | dpcm->timer.expires = jiffies + tick; |
184 | add_timer(&dpcm->timer); | 186 | add_timer(&dpcm->timer); |
187 | spin_unlock(&dpcm->timer_lock); | ||
185 | } | 188 | } |
186 | 189 | ||
187 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | 190 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) |
188 | { | 191 | { |
192 | spin_lock(&dpcm->timer_lock); | ||
189 | del_timer(&dpcm->timer); | 193 | del_timer(&dpcm->timer); |
190 | dpcm->timer.expires = 0; | 194 | dpcm->timer.expires = 0; |
195 | spin_unlock(&dpcm->timer_lock); | ||
191 | } | 196 | } |
192 | 197 | ||
193 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) | 198 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) |
@@ -667,6 +672,7 @@ static int loopback_open(struct snd_pcm_substream *substream) | |||
667 | dpcm->substream = substream; | 672 | dpcm->substream = substream; |
668 | setup_timer(&dpcm->timer, loopback_timer_function, | 673 | setup_timer(&dpcm->timer, loopback_timer_function, |
669 | (unsigned long)dpcm); | 674 | (unsigned long)dpcm); |
675 | spin_lock_init(&dpcm->timer_lock); | ||
670 | 676 | ||
671 | cable = loopback->cables[substream->number][dev]; | 677 | cable = loopback->cables[substream->number][dev]; |
672 | if (!cable) { | 678 | if (!cable) { |