ALSA: hda: Fix racy display power access [Linux 5.1]

ALSA: hda: Fix racy display power access [Linux 5.1]

This Linux kernel change "ALSA: hda: Fix racy display power access" is included in the Linux 5.1 release. This change is authored by Takashi Iwai <tiwai [at] suse.de> on Wed Apr 10 12:49:55 2019 +0200. The commit for this change in Linux stable tree is d7a181d (patch).

ALSA: hda: Fix racy display power access

snd_hdac_display_power() doesn't handle the concurrent calls carefully
enough, and it may lead to the doubly get_power or put_power calls,
when a runtime PM and an async work get called in racy way.

This patch addresses it by reusing the bus->lock mutex that has been
used for protecting the link state change in ext bus code, so that it
can protect against racy display state changes.  The initialization of
bus->lock was moved from snd_hdac_ext_bus_init() to
snd_hdac_bus_init() as well accordingly.

Testcase: igt/i915_pm_rpm/module-reload #glk-dsi
Reported-by: Chris Wilson <[email protected]>
Reviewed-by: Chris Wilson <[email protected]>
Cc: Imre Deak <[email protected]>
Signed-off-by: Takashi Iwai <[email protected]>

There are 8 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 sound/hda/ext/hdac_ext_bus.c | 1 -
 sound/hda/hdac_bus.c         | 1 +
 sound/hda/hdac_component.c   | 6 +++++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 9c37d9a..ec7715c 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -107,7 +107,6 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
    INIT_LIST_HEAD(&bus->hlink_list);
    bus->idx = idx++;

-   mutex_init(&bus->lock);
    bus->cmd_dma_state = true;

    return 0;
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 0123051..ad8eee0 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -38,6 +38,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
    INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
    spin_lock_init(&bus->reg_lock);
    mutex_init(&bus->cmd_mutex);
+   mutex_init(&bus->lock);
    bus->irq = -1;
    return 0;
 }
diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c
index a6d37b9..6b5caee 100644
--- a/sound/hda/hdac_component.c
+++ b/sound/hda/hdac_component.c
@@ -69,13 +69,15 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)

    dev_dbg(bus->dev, "display power %s\n",
        enable ? "enable" : "disable");
+
+   mutex_lock(&bus->lock);
    if (enable)
        set_bit(idx, &bus->display_power_status);
    else
        clear_bit(idx, &bus->display_power_status);

    if (!acomp || !acomp->ops)
-       return;
+       goto unlock;

    if (bus->display_power_status) {
        if (!bus->display_power_active) {
@@ -92,6 +94,8 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
            bus->display_power_active = false;
        }
    }
+ unlock:
+   mutex_unlock(&bus->lock);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_display_power);

Leave a Reply

Your email address will not be published. Required fields are marked *