ALSA: hda - Fix another race in runtime PM refcounting
authorTakashi Iwai <tiwai@suse.de>
Mon, 13 Apr 2015 09:01:14 +0000 (11:01 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 13 Apr 2015 09:07:20 +0000 (11:07 +0200)
Although some races in runtime PM refcount was fixed by the commit
[664c715573c2: ALSA: hda - Work around races of power up/down with
runtime PM], there is still a race in the following case:

CPU0:                   CPU1 :
runtime suspend:
  codec->in_pm = 1
                        snd_hdac_power_up_pm():
                          pm_runtime_get_sync() skipped
suspend finished:
  codec->in_pm = 0
                        snd_hdac_power_down_pm():
                          pm_runtime_put_*() is called!

For avoiding this situation, increment in_pm flag atomically when it's
non-zero, and decrement accordingly, to ensure that in_pm is set
consistently for the whole concurrent operations.

Also, since atomic_inc_not_zero() and atomic_dec_if_positive() are
lengthy inline functions, move snd_hdac_power_up_pm() and _down_pm()
to sound/hda/hdac_device.c as no inline functions.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/hdaudio.h
sound/hda/hdac_device.c

index 30446f17c6a65cda0f34d62ec3f11e2edf07b923..2a8aa9dfb83d73f61d5b7be73926f4850007c39e 100644 (file)
@@ -139,39 +139,15 @@ static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid,
 #ifdef CONFIG_PM
 void snd_hdac_power_up(struct hdac_device *codec);
 void snd_hdac_power_down(struct hdac_device *codec);
+void snd_hdac_power_up_pm(struct hdac_device *codec);
+void snd_hdac_power_down_pm(struct hdac_device *codec);
 #else
 static inline void snd_hdac_power_up(struct hdac_device *codec) {}
 static inline void snd_hdac_power_down(struct hdac_device *codec) {}
+static inline void snd_hdac_power_up_pm(struct hdac_device *codec) {}
+static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {}
 #endif
 
-/**
- * snd_hdac_power_up_pm - power up the codec
- * @codec: the codec object
- *
- * This function can be called in a recursive code path like init code
- * which may be called by PM suspend/resume again.  OTOH, if a power-up
- * call must wake up the sleeper (e.g. in a kctl callback), use
- * snd_hdac_power_up() instead.
- */
-static inline void snd_hdac_power_up_pm(struct hdac_device *codec)
-{
-       if (!atomic_read(&codec->in_pm))
-               snd_hdac_power_up(codec);
-}
-
-/**
- * snd_hdac_power_down_pm - power down the codec
- * @codec: the codec object
- *
- * Like snd_hdac_power_up_pm(), this function is used in a recursive
- * code path like init code which may be called by PM suspend/resume again.
- */
-static inline void snd_hdac_power_down_pm(struct hdac_device *codec)
-{
-       if (!atomic_read(&codec->in_pm))
-               snd_hdac_power_down(codec);
-}
-
 /*
  * HD-audio codec base driver
  */
index 92604bbcee5ff1ee1ab210aa386efad400acf6f9..f75bf56226878318130be8e383f5b91143e5d4c3 100644 (file)
@@ -519,6 +519,36 @@ void snd_hdac_power_down(struct hdac_device *codec)
        pm_runtime_put_autosuspend(dev);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_power_down);
+
+/**
+ * snd_hdac_power_up_pm - power up the codec
+ * @codec: the codec object
+ *
+ * This function can be called in a recursive code path like init code
+ * which may be called by PM suspend/resume again.  OTOH, if a power-up
+ * call must wake up the sleeper (e.g. in a kctl callback), use
+ * snd_hdac_power_up() instead.
+ */
+void snd_hdac_power_up_pm(struct hdac_device *codec)
+{
+       if (!atomic_inc_not_zero(&codec->in_pm))
+               snd_hdac_power_up(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
+
+/**
+ * snd_hdac_power_down_pm - power down the codec
+ * @codec: the codec object
+ *
+ * Like snd_hdac_power_up_pm(), this function is used in a recursive
+ * code path like init code which may be called by PM suspend/resume again.
+ */
+void snd_hdac_power_down_pm(struct hdac_device *codec)
+{
+       if (atomic_dec_if_positive(&codec->in_pm) < 0)
+               snd_hdac_power_down(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
 #endif
 
 /* codec vendor labels */