diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-12-11 06:51:05 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-12-11 06:53:27 -0500 |
commit | fcfdebe70759c74e2e701f69aaa7f0e5e32cf5a6 (patch) | |
tree | 4f31584d288d37d9168544bfe34d81cc4a8eef2f /sound | |
parent | 5f60e496083efb01893a899b6885828330db971f (diff) |
ALSA: hrtimer - Fix lock-up
The timer stop callback can be called from snd_timer_interrupt(), which
is called from the hrtimer callback. Since hrtimer_cancel() waits for
the callback completion, this eventually results in a lock-up.
This patch fixes the problem by just toggling a flag at stop callback
and call hrtimer_cancel() later.
Reported-and-tested-by: Wojtek Zabolotny <W.Zabolotny@elka.pw.edu.pl>
Cc: <stable@kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/hrtimer.c | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 34c7d48f5061..7f4d744ae40a 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c | |||
@@ -37,14 +37,22 @@ static unsigned int resolution; | |||
37 | struct snd_hrtimer { | 37 | struct snd_hrtimer { |
38 | struct snd_timer *timer; | 38 | struct snd_timer *timer; |
39 | struct hrtimer hrt; | 39 | struct hrtimer hrt; |
40 | atomic_t running; | ||
40 | }; | 41 | }; |
41 | 42 | ||
42 | static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) | 43 | static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) |
43 | { | 44 | { |
44 | struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); | 45 | struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); |
45 | struct snd_timer *t = stime->timer; | 46 | struct snd_timer *t = stime->timer; |
47 | |||
48 | if (!atomic_read(&stime->running)) | ||
49 | return HRTIMER_NORESTART; | ||
50 | |||
46 | hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); | 51 | hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); |
47 | snd_timer_interrupt(stime->timer, t->sticks); | 52 | snd_timer_interrupt(stime->timer, t->sticks); |
53 | |||
54 | if (!atomic_read(&stime->running)) | ||
55 | return HRTIMER_NORESTART; | ||
48 | return HRTIMER_RESTART; | 56 | return HRTIMER_RESTART; |
49 | } | 57 | } |
50 | 58 | ||
@@ -58,6 +66,7 @@ static int snd_hrtimer_open(struct snd_timer *t) | |||
58 | hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 66 | hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
59 | stime->timer = t; | 67 | stime->timer = t; |
60 | stime->hrt.function = snd_hrtimer_callback; | 68 | stime->hrt.function = snd_hrtimer_callback; |
69 | atomic_set(&stime->running, 0); | ||
61 | t->private_data = stime; | 70 | t->private_data = stime; |
62 | return 0; | 71 | return 0; |
63 | } | 72 | } |
@@ -78,16 +87,18 @@ static int snd_hrtimer_start(struct snd_timer *t) | |||
78 | { | 87 | { |
79 | struct snd_hrtimer *stime = t->private_data; | 88 | struct snd_hrtimer *stime = t->private_data; |
80 | 89 | ||
90 | atomic_set(&stime->running, 0); | ||
91 | hrtimer_cancel(&stime->hrt); | ||
81 | hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), | 92 | hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), |
82 | HRTIMER_MODE_REL); | 93 | HRTIMER_MODE_REL); |
94 | atomic_set(&stime->running, 1); | ||
83 | return 0; | 95 | return 0; |
84 | } | 96 | } |
85 | 97 | ||
86 | static int snd_hrtimer_stop(struct snd_timer *t) | 98 | static int snd_hrtimer_stop(struct snd_timer *t) |
87 | { | 99 | { |
88 | struct snd_hrtimer *stime = t->private_data; | 100 | struct snd_hrtimer *stime = t->private_data; |
89 | 101 | atomic_set(&stime->running, 0); | |
90 | hrtimer_cancel(&stime->hrt); | ||
91 | return 0; | 102 | return 0; |
92 | } | 103 | } |
93 | 104 | ||