aboutsummaryrefslogtreecommitdiffstats
path: root/sound/i2c/other/ak4113.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-01-13 04:53:20 -0500
committerTakashi Iwai <tiwai@suse.de>2015-01-28 16:29:20 -0500
commit4161b4505f1690358ac0a9ee59845a7887336b21 (patch)
tree627a37684dae207307c59322d7673371d7f346a0 /sound/i2c/other/ak4113.c
parent0767e95bb96d7fdddcd590fb809e6975d93aebc5 (diff)
ALSA: ak411x: Fix stall in work callback
When ak4114 work calls its callback and the callback invokes ak4114_reinit(), it stalls due to flush_delayed_work(). For avoiding this, control the reentrance by introducing a refcount. Also flush_delayed_work() is replaced with cancel_delayed_work_sync(). The exactly same bug is present in ak4113.c and fixed as well. Reported-by: Pavel Hofman <pavel.hofman@ivitera.com> Acked-by: Jaroslav Kysela <perex@perex.cz> Tested-by: Pavel Hofman <pavel.hofman@ivitera.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/i2c/other/ak4113.c')
-rw-r--r--sound/i2c/other/ak4113.c17
1 files changed, 8 insertions, 9 deletions
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
index 1a3a6fa27158..c6bba99a90b2 100644
--- a/sound/i2c/other/ak4113.c
+++ b/sound/i2c/other/ak4113.c
@@ -56,8 +56,7 @@ static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
56 56
57static void snd_ak4113_free(struct ak4113 *chip) 57static void snd_ak4113_free(struct ak4113 *chip)
58{ 58{
59 chip->init = 1; /* don't schedule new work */ 59 atomic_inc(&chip->wq_processing); /* don't schedule new work */
60 mb();
61 cancel_delayed_work_sync(&chip->work); 60 cancel_delayed_work_sync(&chip->work);
62 kfree(chip); 61 kfree(chip);
63} 62}
@@ -89,6 +88,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
89 chip->write = write; 88 chip->write = write;
90 chip->private_data = private_data; 89 chip->private_data = private_data;
91 INIT_DELAYED_WORK(&chip->work, ak4113_stats); 90 INIT_DELAYED_WORK(&chip->work, ak4113_stats);
91 atomic_set(&chip->wq_processing, 0);
92 92
93 for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++) 93 for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
94 chip->regmap[reg] = pgm[reg]; 94 chip->regmap[reg] = pgm[reg];
@@ -139,13 +139,11 @@ static void ak4113_init_regs(struct ak4113 *chip)
139 139
140void snd_ak4113_reinit(struct ak4113 *chip) 140void snd_ak4113_reinit(struct ak4113 *chip)
141{ 141{
142 chip->init = 1; 142 if (atomic_inc_return(&chip->wq_processing) == 1)
143 mb(); 143 cancel_delayed_work_sync(&chip->work);
144 flush_delayed_work(&chip->work);
145 ak4113_init_regs(chip); 144 ak4113_init_regs(chip);
146 /* bring up statistics / event queing */ 145 /* bring up statistics / event queing */
147 chip->init = 0; 146 if (atomic_dec_and_test(&chip->wq_processing))
148 if (chip->kctls[0])
149 schedule_delayed_work(&chip->work, HZ / 10); 147 schedule_delayed_work(&chip->work, HZ / 10);
150} 148}
151EXPORT_SYMBOL_GPL(snd_ak4113_reinit); 149EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
@@ -632,8 +630,9 @@ static void ak4113_stats(struct work_struct *work)
632{ 630{
633 struct ak4113 *chip = container_of(work, struct ak4113, work.work); 631 struct ak4113 *chip = container_of(work, struct ak4113, work.work);
634 632
635 if (!chip->init) 633 if (atomic_inc_return(&chip->wq_processing) == 1)
636 snd_ak4113_check_rate_and_errors(chip, chip->check_flags); 634 snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
637 635
638 schedule_delayed_work(&chip->work, HZ / 10); 636 if (atomic_dec_and_test(&chip->wq_processing))
637 schedule_delayed_work(&chip->work, HZ / 10);
639} 638}