aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/seq/seq_timer.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-11-07 20:20:08 +0100
committerTakashi Iwai <tiwai@suse.de>2019-11-08 14:52:44 +0100
commit6a34367e52caea1413eb0a0dcbb524f0c9b67e82 (patch)
treea52eda441990007a9143d0ecc1f286541bd4a075 /sound/core/seq/seq_timer.c
parentALSA: timer: Make snd_timer_close() returning void (diff)
downloadlinux-dev-6a34367e52caea1413eb0a0dcbb524f0c9b67e82.tar.xz
linux-dev-6a34367e52caea1413eb0a0dcbb524f0c9b67e82.zip
ALSA: timer: Fix possible race at assigning a timer instance
When a new timer instance is created and assigned to the active link in snd_timer_open(), the caller still doesn't (can't) set its callback and callback data. In both the user-timer and the sequencer-timer code, they do manually set up the callbacks after calling snd_timer_open(). This has a potential risk of race when the timer instance is added to the already running timer target, as the callback might get triggered during setting up the callback itself. This patch tries to address it by changing the API usage slightly: - An empty timer instance is created at first via the new function snd_timer_instance_new(). This object isn't linked to the timer list yet. - The caller sets up the callbacks and others stuff for the new timer instance. - The caller invokes snd_timer_open() with this instance, so that it's linked to the target timer. For closing, do similarly: - Call snd_timer_close(). This unlinks the timer instance from the timer list. - Free the timer instance via snd_timer_instance_free() after that. Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://lore.kernel.org/r/20191107192008.32331-4-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to '')
-rw-r--r--sound/core/seq/seq_timer.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 161f3170bd7e..63dc7bdb622d 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -272,7 +272,13 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
return -EINVAL;
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
- err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
+ t = snd_timer_instance_new(str);
+ if (!t)
+ return -ENOMEM;
+ t->callback = snd_seq_timer_interrupt;
+ t->callback_data = q;
+ t->flags |= SNDRV_TIMER_IFLG_AUTO;
+ err = snd_timer_open(t, &tmr->alsa_id, q->queue);
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
@@ -282,16 +288,14 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
- err = snd_timer_open(&t, str, &tid, q->queue);
+ err = snd_timer_open(t, &tid, q->queue);
}
}
if (err < 0) {
pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
+ snd_timer_instance_free(t);
return err;
}
- t->callback = snd_seq_timer_interrupt;
- t->callback_data = q;
- t->flags |= SNDRV_TIMER_IFLG_AUTO;
spin_lock_irq(&tmr->lock);
tmr->timeri = t;
spin_unlock_irq(&tmr->lock);
@@ -310,8 +314,10 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
t = tmr->timeri;
tmr->timeri = NULL;
spin_unlock_irq(&tmr->lock);
- if (t)
+ if (t) {
snd_timer_close(t);
+ snd_timer_instance_free(t);
+ }
return 0;
}