ALSA: hda - Protect the power-saving count with spinlock
authorTakashi Iwai <tiwai@suse.de>
Tue, 8 May 2012 15:08:10 +0000 (17:08 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 8 May 2012 16:01:30 +0000 (18:01 +0200)
To avoid some races.  Still not perfect, but now a bit safer.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h

index 8bd34320ef91f0a6d883a2fc1468db63bec164c3..30a3d8ff978b4f22a5c445795f72e41fa4535b84 100644 (file)
@@ -1266,6 +1266,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spin_lock_init(&codec->power_lock);
        INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
        /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
         * the caller has to power down appropriatley after initialization
@@ -4292,12 +4293,16 @@ static void hda_power_work(struct work_struct *work)
                container_of(work, struct hda_codec, power_work.work);
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
        if (!codec->power_on || codec->power_count) {
                codec->power_transition = 0;
+               spin_unlock(&codec->power_lock);
                return;
        }
 
        trace_hda_power_down(codec);
+       spin_unlock(&codec->power_lock);
+
        hda_call_codec_suspend(codec);
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
@@ -4305,9 +4310,11 @@ static void hda_power_work(struct work_struct *work)
 
 static void hda_keep_power_on(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        codec->power_count++;
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
+       spin_unlock(&codec->power_lock);
 }
 
 /* update the power on/off account with the current jiffies */
@@ -4332,20 +4339,28 @@ void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
        codec->power_count++;
-       if (codec->power_on || codec->power_transition)
+       if (codec->power_on || codec->power_transition) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
 
        trace_hda_power_up(codec);
        snd_hda_update_power_acct(codec);
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
        codec->power_transition = 1; /* avoid reentrance */
+       spin_unlock(&codec->power_lock);
+
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
+
+       spin_lock(&codec->power_lock);
        cancel_delayed_work(&codec->power_work);
        codec->power_transition = 0;
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_up);
 
@@ -4361,14 +4376,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
  */
 void snd_hda_power_down(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        --codec->power_count;
-       if (!codec->power_on || codec->power_count || codec->power_transition)
+       if (!codec->power_on || codec->power_count || codec->power_transition) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
        if (power_save(codec)) {
                codec->power_transition = 1; /* avoid reentrance */
                queue_delayed_work(codec->bus->workq, &codec->power_work,
                                msecs_to_jiffies(power_save(codec) * 1000));
        }
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
index 0fe64912cefc0a5d811debc25c1c847487aacc84..77cd99531aae949372ea61a719e08945ca6f218e 100644 (file)
@@ -867,6 +867,7 @@ struct hda_codec {
        unsigned long power_on_acct;
        unsigned long power_off_acct;
        unsigned long power_jiffies;
+       spinlock_t power_lock;
 #endif
 
        /* codec-specific additional proc output */