hda codec unbind refcount hang
Hi Takashi,
commit 7206998f578d ("ALSA: hda: Fix potential deadlock at codec unbinding") introduced a problem on at least one of my older machines.
The problem happens when hda_codec_driver_remove() encounters a codec without any pcms (and thus the refcount is 1) and tries to call refcount_dec(). Turns out refcount_dec() doesn't like to be used for dropping the refcount to 0, and instead if spews a warning and does its saturate thing. The subsequent wait_event() is then permanently stuck waiting on the saturated refcount.
I've definitely seen the same kind of pattern used elsewhere in the kernel as well, so the fact that refcount_t can't be used to implement it is a bit of surprise to me. I guess most other places still use atomic_t instead.
Signature · 2 lines
Ville Syrjälä Intel
Quoted 13 lines · Ville Syrjälä
Hi Takashi,
commit
7206998f578d("ALSA: hda: Fix potential deadlock at codec unbinding") introduced a problem on at least one of my older machines.The problem happens when
hda_codec_driver_remove()encounters a codec without any pcms (and thus the refcount is 1) and tries to callrefcount_dec(). Turns outrefcount_dec()doesn't like to be used for dropping the refcount to 0, and instead if spews a warning and does its saturate thing. The subsequentwait_event()is then permanently stuck waiting on the saturated refcount.
Quoted 4 lines
I've definitely seen the same kind of pattern used elsewhere in the kernel as well, so the fact that
refcount_tcan't be used to implement it is a bit of surprise to me. I guess most other places still useatomic_tinstead.
Does the patch below work around it? It seem to be a subtle difference between refcount_dec() and refcount_dec_and_test().
thanks,
Takashi
-- 8< --
sound/pci/hda/hda_bind.c +5 -4
@@ -157,10 +157,11 @@ static int hda_codec_driver_remove(struct device *dev)return codec->bus->core.ext_ops->hdev_detach (&codec->core); } -refcount_dec (&codec->pcm_ref); -snd_hda_codec_disconnect_pcms (codec); -snd_hda_jack_tbl_disconnect (codec); -wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); +if (!refcount_dec_and_test (&codec->pcm_ref)) { +snd_hda_codec_disconnect_pcms (codec); +snd_hda_jack_tbl_disconnect (codec); +wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); + }snd_power_sync_ref (codec->bus->card);if (codec->patch_ops.free)
Ville Syrjälä
Tested-byQuoted 15 lines · Takashi Iwai
Quoted 13 lines · Ville Syrjälä
Hi Takashi,
commit
7206998f578d("ALSA: hda: Fix potential deadlock at codec unbinding") introduced a problem on at least one of my older machines.The problem happens when
hda_codec_driver_remove()encounters a codec without any pcms (and thus the refcount is 1) and tries to callrefcount_dec(). Turns outrefcount_dec()doesn't like to be used for dropping the refcount to 0, and instead if spews a warning and does its saturate thing. The subsequentwait_event()is then permanently stuck waiting on the saturated refcount.
Quoted 7 lines
Quoted 4 lines
I've definitely seen the same kind of pattern used elsewhere in the kernel as well, so the fact that
refcount_tcan't be used to implement it is a bit of surprise to me. I guess most other places still useatomic_tinstead.Does the patch below work around it? It seem to be a subtle difference between
refcount_dec()andrefcount_dec_and_test().
Aye, this works.
Quoted 25 lines
thanks,
Takashi
-- 8< --
sound/pci/hda/hda_bind.c +5
-4@@ -157,10 +157,11 @@ static int hda_codec_driver_remove(struct device *dev)return codec->bus->core.ext_ops->hdev_detach (&codec->core); } -refcount_dec (&codec->pcm_ref); -snd_hda_codec_disconnect_pcms (codec); -snd_hda_jack_tbl_disconnect (codec); -wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); +if (!refcount_dec_and_test (&codec->pcm_ref)) { +snd_hda_codec_disconnect_pcms (codec); +snd_hda_jack_tbl_disconnect (codec); +wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); + }snd_power_sync_ref (codec->bus->card);if (codec->patch_ops.free)
Signature · 2 lines
Ville Syrjälä Intel
Quoted 22 lines · Ville Syrjälä
Quoted 20 lines · Takashi Iwai
Quoted 17 lines · Ville Syrjälä
Hi Takashi,
commit
7206998f578d("ALSA: hda: Fix potential deadlock at codec unbinding") introduced a problem on at least one of my older machines.The problem happens when
hda_codec_driver_remove()encounters a codec without any pcms (and thus the refcount is 1) and tries to callrefcount_dec(). Turns outrefcount_dec()doesn't like to be used for dropping the refcount to 0, and instead if spews a warning and does its saturate thing. The subsequentwait_event()is then permanently stuck waiting on the saturated refcount.I've definitely seen the same kind of pattern used elsewhere in the kernel as well, so the fact that
refcount_tcan't be used to implement it is a bit of surprise to me. I guess most other places still useatomic_tinstead.
Quoted 6 lines
Quoted 2 lines
Does the patch below work around it? It seem to be a subtle difference between
refcount_dec()andrefcount_dec_and_test().Aye, this works.
Tested-by Ville Syrjälä <[email protected]>
Good to hear.
I think the below is slightly safer, assuring the other *_disconnect() calls.
Could you give it a try again? Once after confirming it works, I'll re-submit and merge to my tree.
thanks,
Takashi
-- 8< --
From: Takashi Iwai <[email protected]>
Subject: [PATCH] ALSA: hda: Fix hang at HD-audio codec unbinding due to refcount saturation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We fixed the potential deadlock at dynamic unbinding the HD-audio codec at the commit 7206998f578d ("ALSA: hda: Fix potential deadlock at codec unbinding"), but ironically, this caused another potential deadlock. The current code uses refcount_dec() and waits for the pending task with wait_event for dropping the refcount to 0. This works fine when PCMs are assigned and actually waiting for the refcount drop.
Meanwhile, when there was no PCM assigned, the refcount_dec() call itself was supposed to drop to zero -- alas, it doesn't in reality; refcount_dec() complains, spews kernel warning and it saturates instead of dropping to 0, due to the nature of refcount_dec() implementation. This eventually blocks the wait_event() wakeup and the code get stuck there.
For avoiding the problem, we call refcount_dec_and_test() and skips the sync-wait if it already reaches to zero.
The patch does a slight code reshuffling to make sure to invoke other disconnect calls before the sync-wait, too.
--- sound/pci/hda/hda_bind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/pci/hda/hda_bind.c +2 -2
@@ -157,10 +157,10 @@ static int hda_codec_driver_remove(struct device *dev)return codec->bus->core.ext_ops->hdev_detach (&codec->core); } -refcount_dec (&codec->pcm_ref);snd_hda_codec_disconnect_pcms (codec);snd_hda_jack_tbl_disconnect (codec); -wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); +if (!refcount_dec_and_test (&codec->pcm_ref)) +wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref));snd_power_sync_ref (codec->bus->card);if (codec->patch_ops.free)
Signature · 1 line
2.35.3
Ville Syrjälä
Tested-byQuoted 31 lines · Takashi Iwai
Quoted 22 lines · Ville Syrjälä
Quoted 20 lines · Takashi Iwai
Quoted 17 lines · Ville Syrjälä
Hi Takashi,
commit
7206998f578d("ALSA: hda: Fix potential deadlock at codec unbinding") introduced a problem on at least one of my older machines.The problem happens when
hda_codec_driver_remove()encounters a codec without any pcms (and thus the refcount is 1) and tries to callrefcount_dec(). Turns outrefcount_dec()doesn't like to be used for dropping the refcount to 0, and instead if spews a warning and does its saturate thing. The subsequentwait_event()is then permanently stuck waiting on the saturated refcount.I've definitely seen the same kind of pattern used elsewhere in the kernel as well, so the fact that
refcount_tcan't be used to implement it is a bit of surprise to me. I guess most other places still useatomic_tinstead.Quoted 6 lines
Quoted 2 lines
Does the patch below work around it? It seem to be a subtle difference between
refcount_dec()andrefcount_dec_and_test().Aye, this works.
Tested-by Ville Syrjälä <[email protected]>
Quoted 7 lines
Good to hear.
I think the below is slightly safer, assuring the other
*_disconnect()calls.Could you give it a try again? Once after confirming it works, I'll re-submit and merge to my tree.
This works too. Thanks
Quoted 62 lines
thanks,
Takashi
-- 8< --
From: Takashi Iwai <[email protected]>
Subject: [PATCH] ALSA: hda: Fix hang at HD-audio codec unbinding due to refcount saturation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bitWe fixed the potential deadlock at dynamic unbinding the HD-audio codec at the commit
7206998f578d("ALSA: hda: Fix potential deadlock at codec unbinding"), but ironically, this caused another potential deadlock. The current code usesrefcount_dec()and waits for the pending task withwait_eventfor dropping the refcount to 0. This works fine when PCMs are assigned and actually waiting for the refcount drop.Meanwhile, when there was no PCM assigned, the
refcount_dec()call itself was supposed to drop to zero -- alas, it doesn't in reality;refcount_dec()complains, spews kernel warning and it saturates instead of dropping to 0, due to the nature ofrefcount_dec()implementation. This eventually blocks thewait_event()wakeup and the code get stuck there.For avoiding the problem, we call
refcount_dec_and_test()and skips the sync-wait if it already reaches to zero.The patch does a slight code reshuffling to make sure to invoke other disconnect calls before the sync-wait, too.
Fixes 7206998f578d ("ALSA: hda: Fix potential deadlock at codec unbinding")Reported-by Ville Syrjälä <[email protected]>Cc <[email protected]>Signed-off-by Takashi Iwai <[email protected]>--- sound/pci/hda/hda_bind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)sound/pci/hda/hda_bind.c +2
-2@@ -157,10 +157,10 @@ static int hda_codec_driver_remove(struct device *dev)return codec->bus->core.ext_ops->hdev_detach (&codec->core); } -refcount_dec (&codec->pcm_ref);snd_hda_codec_disconnect_pcms (codec);snd_hda_jack_tbl_disconnect (codec); -wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref)); +if (!refcount_dec_and_test (&codec->pcm_ref)) +wait_event (codec->remove_sleep, !refcount_read (&codec->pcm_ref));snd_power_sync_ref (codec->bus->card);if (codec->patch_ops.free)Signature · 1 line
2.35.3
Signature · 2 lines
Ville Syrjälä Intel