diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-05-08 11:08:10 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-08 12:01:30 -0400 |
commit | 5536c6d69376273d5210ce05b7d5d462f0dff9d6 (patch) | |
tree | d65dd8459225d8d6eb49ba8e3f321120274bea14 /sound/pci | |
parent | 339876d70a5794c0d5fe09d37827c63148d5017a (diff) |
ALSA: hda - Protect the power-saving count with spinlock
To avoid some races. Still not perfect, but now a bit safer.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 23 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 1 |
2 files changed, 22 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8bd34320ef91..30a3d8ff978b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -1266,6 +1266,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, | |||
1266 | snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); | 1266 | snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); |
1267 | 1267 | ||
1268 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1268 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1269 | spin_lock_init(&codec->power_lock); | ||
1269 | INIT_DELAYED_WORK(&codec->power_work, hda_power_work); | 1270 | INIT_DELAYED_WORK(&codec->power_work, hda_power_work); |
1270 | /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. | 1271 | /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. |
1271 | * the caller has to power down appropriatley after initialization | 1272 | * the caller has to power down appropriatley after initialization |
@@ -4292,12 +4293,16 @@ static void hda_power_work(struct work_struct *work) | |||
4292 | container_of(work, struct hda_codec, power_work.work); | 4293 | container_of(work, struct hda_codec, power_work.work); |
4293 | struct hda_bus *bus = codec->bus; | 4294 | struct hda_bus *bus = codec->bus; |
4294 | 4295 | ||
4296 | spin_lock(&codec->power_lock); | ||
4295 | if (!codec->power_on || codec->power_count) { | 4297 | if (!codec->power_on || codec->power_count) { |
4296 | codec->power_transition = 0; | 4298 | codec->power_transition = 0; |
4299 | spin_unlock(&codec->power_lock); | ||
4297 | return; | 4300 | return; |
4298 | } | 4301 | } |
4299 | 4302 | ||
4300 | trace_hda_power_down(codec); | 4303 | trace_hda_power_down(codec); |
4304 | spin_unlock(&codec->power_lock); | ||
4305 | |||
4301 | hda_call_codec_suspend(codec); | 4306 | hda_call_codec_suspend(codec); |
4302 | if (bus->ops.pm_notify) | 4307 | if (bus->ops.pm_notify) |
4303 | bus->ops.pm_notify(bus); | 4308 | bus->ops.pm_notify(bus); |
@@ -4305,9 +4310,11 @@ static void hda_power_work(struct work_struct *work) | |||
4305 | 4310 | ||
4306 | static void hda_keep_power_on(struct hda_codec *codec) | 4311 | static void hda_keep_power_on(struct hda_codec *codec) |
4307 | { | 4312 | { |
4313 | spin_lock(&codec->power_lock); | ||
4308 | codec->power_count++; | 4314 | codec->power_count++; |
4309 | codec->power_on = 1; | 4315 | codec->power_on = 1; |
4310 | codec->power_jiffies = jiffies; | 4316 | codec->power_jiffies = jiffies; |
4317 | spin_unlock(&codec->power_lock); | ||
4311 | } | 4318 | } |
4312 | 4319 | ||
4313 | /* update the power on/off account with the current jiffies */ | 4320 | /* update the power on/off account with the current jiffies */ |
@@ -4332,20 +4339,28 @@ void snd_hda_power_up(struct hda_codec *codec) | |||
4332 | { | 4339 | { |
4333 | struct hda_bus *bus = codec->bus; | 4340 | struct hda_bus *bus = codec->bus; |
4334 | 4341 | ||
4342 | spin_lock(&codec->power_lock); | ||
4335 | codec->power_count++; | 4343 | codec->power_count++; |
4336 | if (codec->power_on || codec->power_transition) | 4344 | if (codec->power_on || codec->power_transition) { |
4345 | spin_unlock(&codec->power_lock); | ||
4337 | return; | 4346 | return; |
4347 | } | ||
4338 | 4348 | ||
4339 | trace_hda_power_up(codec); | 4349 | trace_hda_power_up(codec); |
4340 | snd_hda_update_power_acct(codec); | 4350 | snd_hda_update_power_acct(codec); |
4341 | codec->power_on = 1; | 4351 | codec->power_on = 1; |
4342 | codec->power_jiffies = jiffies; | 4352 | codec->power_jiffies = jiffies; |
4343 | codec->power_transition = 1; /* avoid reentrance */ | 4353 | codec->power_transition = 1; /* avoid reentrance */ |
4354 | spin_unlock(&codec->power_lock); | ||
4355 | |||
4344 | if (bus->ops.pm_notify) | 4356 | if (bus->ops.pm_notify) |
4345 | bus->ops.pm_notify(bus); | 4357 | bus->ops.pm_notify(bus); |
4346 | hda_call_codec_resume(codec); | 4358 | hda_call_codec_resume(codec); |
4359 | |||
4360 | spin_lock(&codec->power_lock); | ||
4347 | cancel_delayed_work(&codec->power_work); | 4361 | cancel_delayed_work(&codec->power_work); |
4348 | codec->power_transition = 0; | 4362 | codec->power_transition = 0; |
4363 | spin_unlock(&codec->power_lock); | ||
4349 | } | 4364 | } |
4350 | EXPORT_SYMBOL_HDA(snd_hda_power_up); | 4365 | EXPORT_SYMBOL_HDA(snd_hda_power_up); |
4351 | 4366 | ||
@@ -4361,14 +4376,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); | |||
4361 | */ | 4376 | */ |
4362 | void snd_hda_power_down(struct hda_codec *codec) | 4377 | void snd_hda_power_down(struct hda_codec *codec) |
4363 | { | 4378 | { |
4379 | spin_lock(&codec->power_lock); | ||
4364 | --codec->power_count; | 4380 | --codec->power_count; |
4365 | if (!codec->power_on || codec->power_count || codec->power_transition) | 4381 | if (!codec->power_on || codec->power_count || codec->power_transition) { |
4382 | spin_unlock(&codec->power_lock); | ||
4366 | return; | 4383 | return; |
4384 | } | ||
4367 | if (power_save(codec)) { | 4385 | if (power_save(codec)) { |
4368 | codec->power_transition = 1; /* avoid reentrance */ | 4386 | codec->power_transition = 1; /* avoid reentrance */ |
4369 | queue_delayed_work(codec->bus->workq, &codec->power_work, | 4387 | queue_delayed_work(codec->bus->workq, &codec->power_work, |
4370 | msecs_to_jiffies(power_save(codec) * 1000)); | 4388 | msecs_to_jiffies(power_save(codec) * 1000)); |
4371 | } | 4389 | } |
4390 | spin_unlock(&codec->power_lock); | ||
4372 | } | 4391 | } |
4373 | EXPORT_SYMBOL_HDA(snd_hda_power_down); | 4392 | EXPORT_SYMBOL_HDA(snd_hda_power_down); |
4374 | 4393 | ||
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0fe64912cefc..77cd99531aae 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -867,6 +867,7 @@ struct hda_codec { | |||
867 | unsigned long power_on_acct; | 867 | unsigned long power_on_acct; |
868 | unsigned long power_off_acct; | 868 | unsigned long power_off_acct; |
869 | unsigned long power_jiffies; | 869 | unsigned long power_jiffies; |
870 | spinlock_t power_lock; | ||
870 | #endif | 871 | #endif |
871 | 872 | ||
872 | /* codec-specific additional proc output */ | 873 | /* codec-specific additional proc output */ |