]>
Commit | Line | Data |
---|---|---|
7d09d525 GKH |
1 | From 4161b4505f1690358ac0a9ee59845a7887336b21 Mon Sep 17 00:00:00 2001 |
2 | From: Takashi Iwai <tiwai@suse.de> | |
3 | Date: Tue, 13 Jan 2015 10:53:20 +0100 | |
4 | Subject: ALSA: ak411x: Fix stall in work callback | |
5 | ||
6 | From: Takashi Iwai <tiwai@suse.de> | |
7 | ||
8 | commit 4161b4505f1690358ac0a9ee59845a7887336b21 upstream. | |
9 | ||
10 | When ak4114 work calls its callback and the callback invokes | |
11 | ak4114_reinit(), it stalls due to flush_delayed_work(). For avoiding | |
12 | this, control the reentrance by introducing a refcount. Also | |
13 | flush_delayed_work() is replaced with cancel_delayed_work_sync(). | |
14 | ||
15 | The exactly same bug is present in ak4113.c and fixed as well. | |
16 | ||
17 | Reported-by: Pavel Hofman <pavel.hofman@ivitera.com> | |
18 | Acked-by: Jaroslav Kysela <perex@perex.cz> | |
19 | Tested-by: Pavel Hofman <pavel.hofman@ivitera.com> | |
20 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
21 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
22 | ||
23 | --- | |
24 | include/sound/ak4113.h | 2 +- | |
25 | include/sound/ak4114.h | 2 +- | |
26 | sound/i2c/other/ak4113.c | 17 ++++++++--------- | |
27 | sound/i2c/other/ak4114.c | 18 ++++++++---------- | |
28 | 4 files changed, 18 insertions(+), 21 deletions(-) | |
29 | ||
30 | --- a/include/sound/ak4113.h | |
31 | +++ b/include/sound/ak4113.h | |
32 | @@ -286,7 +286,7 @@ struct ak4113 { | |
33 | ak4113_write_t *write; | |
34 | ak4113_read_t *read; | |
35 | void *private_data; | |
36 | - unsigned int init:1; | |
37 | + atomic_t wq_processing; | |
38 | spinlock_t lock; | |
39 | unsigned char regmap[AK4113_WRITABLE_REGS]; | |
40 | struct snd_kcontrol *kctls[AK4113_CONTROLS]; | |
41 | --- a/include/sound/ak4114.h | |
42 | +++ b/include/sound/ak4114.h | |
43 | @@ -168,7 +168,7 @@ struct ak4114 { | |
44 | ak4114_write_t * write; | |
45 | ak4114_read_t * read; | |
46 | void * private_data; | |
47 | - unsigned int init: 1; | |
48 | + atomic_t wq_processing; | |
49 | spinlock_t lock; | |
50 | unsigned char regmap[7]; | |
51 | unsigned char txcsb[5]; | |
52 | --- a/sound/i2c/other/ak4113.c | |
53 | +++ b/sound/i2c/other/ak4113.c | |
54 | @@ -56,8 +56,7 @@ static inline unsigned char reg_read(str | |
55 | ||
56 | static void snd_ak4113_free(struct ak4113 *chip) | |
57 | { | |
58 | - chip->init = 1; /* don't schedule new work */ | |
59 | - mb(); | |
60 | + atomic_inc(&chip->wq_processing); /* don't schedule new work */ | |
61 | cancel_delayed_work_sync(&chip->work); | |
62 | kfree(chip); | |
63 | } | |
64 | @@ -89,6 +88,7 @@ int snd_ak4113_create(struct snd_card *c | |
65 | chip->write = write; | |
66 | chip->private_data = private_data; | |
67 | INIT_DELAYED_WORK(&chip->work, ak4113_stats); | |
68 | + atomic_set(&chip->wq_processing, 0); | |
69 | ||
70 | for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++) | |
71 | chip->regmap[reg] = pgm[reg]; | |
72 | @@ -139,13 +139,11 @@ static void ak4113_init_regs(struct ak41 | |
73 | ||
74 | void snd_ak4113_reinit(struct ak4113 *chip) | |
75 | { | |
76 | - chip->init = 1; | |
77 | - mb(); | |
78 | - flush_delayed_work(&chip->work); | |
79 | + if (atomic_inc_return(&chip->wq_processing) == 1) | |
80 | + cancel_delayed_work_sync(&chip->work); | |
81 | ak4113_init_regs(chip); | |
82 | /* bring up statistics / event queing */ | |
83 | - chip->init = 0; | |
84 | - if (chip->kctls[0]) | |
85 | + if (atomic_dec_and_test(&chip->wq_processing)) | |
86 | schedule_delayed_work(&chip->work, HZ / 10); | |
87 | } | |
88 | EXPORT_SYMBOL_GPL(snd_ak4113_reinit); | |
89 | @@ -632,8 +630,9 @@ static void ak4113_stats(struct work_str | |
90 | { | |
91 | struct ak4113 *chip = container_of(work, struct ak4113, work.work); | |
92 | ||
93 | - if (!chip->init) | |
94 | + if (atomic_inc_return(&chip->wq_processing) == 1) | |
95 | snd_ak4113_check_rate_and_errors(chip, chip->check_flags); | |
96 | ||
97 | - schedule_delayed_work(&chip->work, HZ / 10); | |
98 | + if (atomic_dec_and_test(&chip->wq_processing)) | |
99 | + schedule_delayed_work(&chip->work, HZ / 10); | |
100 | } | |
101 | --- a/sound/i2c/other/ak4114.c | |
102 | +++ b/sound/i2c/other/ak4114.c | |
103 | @@ -66,8 +66,7 @@ static void reg_dump(struct ak4114 *ak41 | |
104 | ||
105 | static void snd_ak4114_free(struct ak4114 *chip) | |
106 | { | |
107 | - chip->init = 1; /* don't schedule new work */ | |
108 | - mb(); | |
109 | + atomic_inc(&chip->wq_processing); /* don't schedule new work */ | |
110 | cancel_delayed_work_sync(&chip->work); | |
111 | kfree(chip); | |
112 | } | |
113 | @@ -100,6 +99,7 @@ int snd_ak4114_create(struct snd_card *c | |
114 | chip->write = write; | |
115 | chip->private_data = private_data; | |
116 | INIT_DELAYED_WORK(&chip->work, ak4114_stats); | |
117 | + atomic_set(&chip->wq_processing, 0); | |
118 | ||
119 | for (reg = 0; reg < 7; reg++) | |
120 | chip->regmap[reg] = pgm[reg]; | |
121 | @@ -152,13 +152,11 @@ static void ak4114_init_regs(struct ak41 | |
122 | ||
123 | void snd_ak4114_reinit(struct ak4114 *chip) | |
124 | { | |
125 | - chip->init = 1; | |
126 | - mb(); | |
127 | - flush_delayed_work(&chip->work); | |
128 | + if (atomic_inc_return(&chip->wq_processing) == 1) | |
129 | + cancel_delayed_work_sync(&chip->work); | |
130 | ak4114_init_regs(chip); | |
131 | /* bring up statistics / event queing */ | |
132 | - chip->init = 0; | |
133 | - if (chip->kctls[0]) | |
134 | + if (atomic_dec_and_test(&chip->wq_processing)) | |
135 | schedule_delayed_work(&chip->work, HZ / 10); | |
136 | } | |
137 | ||
138 | @@ -612,10 +610,10 @@ static void ak4114_stats(struct work_str | |
139 | { | |
140 | struct ak4114 *chip = container_of(work, struct ak4114, work.work); | |
141 | ||
142 | - if (!chip->init) | |
143 | + if (atomic_inc_return(&chip->wq_processing) == 1) | |
144 | snd_ak4114_check_rate_and_errors(chip, chip->check_flags); | |
145 | - | |
146 | - schedule_delayed_work(&chip->work, HZ / 10); | |
147 | + if (atomic_dec_and_test(&chip->wq_processing)) | |
148 | + schedule_delayed_work(&chip->work, HZ / 10); | |
149 | } | |
150 | ||
151 | EXPORT_SYMBOL(snd_ak4114_create); |