Patchwork [3.5.yuz,extended,stable] Patch "ALSA: hda - Fix hang caused by race during suspend." has been added to staging queue

mail settings
Submitter Herton Ronaldo Krzesinski
Date Nov. 15, 2012, 5:49 a.m.
Message ID <>
Download mbox | patch
Permalink /patch/199163/
State New
Headers show


Herton Ronaldo Krzesinski - Nov. 15, 2012, 5:49 a.m.
This is a note to let you know that I have just added a patch titled

    ALSA: hda - Fix hang caused by race during suspend.

to the linux-3.5.y-queue branch of the 3.5.yuz extended stable tree 
which can be found at:;a=shortlog;h=refs/heads/linux-3.5.y-queue

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.5.yuz tree, see



From fea3a15a30517e7de8048c64338d90316bd7b350 Mon Sep 17 00:00:00 2001
From: Dylan Reid <>
Date: Fri, 28 Sep 2012 15:57:01 -0700
Subject: [PATCH] ALSA: hda - Fix hang caused by race during suspend.

commit d17344b3547669f5b6ee4fda993d03737a141bd6 upstream.

There was a race condition when the system suspends while hda_power_work
is running in the work queue.  If system suspend (snd_hda_suspend)
happens after the work queue releases power_lock but before it calls
hda_call_codec_suspend,  codec_suspend runs with power_on=0, causing the
codec to power up for register reads, and hanging when it calls
cancel_delayed_work_sync from the running work queue.

The call chain from the work queue will look like this:
hda_power_work <<- power_on = 1, unlock, then power_on cleard by suspend
	        cancel_delayed_work_sync <<-- cancelling executing wq

Fix this by waiting for the work queue to finish before starting suspend
if suspend is not happening on the work queue.

Signed-off-by: Dylan Reid <>
Signed-off-by: Takashi Iwai <>
[ herton: backported to 3.5:
  * hda_call_codec_suspend doesn't return state
  * adjusted context ]
Signed-off-by: Herton Ronaldo Krzesinski <>
 sound/pci/hda/hda_codec.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)



diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index f1c6164..b1b6238 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -3542,7 +3542,7 @@  static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
  * call suspend and power-down; used both from PM and power-save
-static void hda_call_codec_suspend(struct hda_codec *codec)
+static void hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
 	if (codec->patch_ops.suspend)
 		codec->patch_ops.suspend(codec, PMSG_SUSPEND);
@@ -3551,7 +3551,9 @@  static void hda_call_codec_suspend(struct hda_codec *codec)
 			    codec->afg ? codec->afg : codec->mfg,
-	cancel_delayed_work(&codec->power_work);
+	/* Cancel delayed work if we aren't currently running from it. */
+	if (!in_wq)
+		cancel_delayed_work_sync(&codec->power_work);
@@ -4372,7 +4374,7 @@  static void hda_power_work(struct work_struct *work)

-	hda_call_codec_suspend(codec);
+	hda_call_codec_suspend(codec, true);
 	if (bus->ops.pm_notify)
@@ -5038,7 +5040,7 @@  int snd_hda_suspend(struct hda_bus *bus)

 	list_for_each_entry(codec, &bus->codec_list, list) {
 		if (hda_codec_is_power_on(codec))
-			hda_call_codec_suspend(codec);
+			hda_call_codec_suspend(codec, false);
 	return 0;