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 <chris@chris-wilson.co.uk>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2019-04-10 12:49:55 +02:00
parent cae3052790
commit d7a181da2d
3 changed files with 6 additions and 2 deletions

View File

@ -107,7 +107,6 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
INIT_LIST_HEAD(&bus->hlink_list); INIT_LIST_HEAD(&bus->hlink_list);
bus->idx = idx++; bus->idx = idx++;
mutex_init(&bus->lock);
bus->cmd_dma_state = true; bus->cmd_dma_state = true;
return 0; return 0;

View File

@ -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); INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
spin_lock_init(&bus->reg_lock); spin_lock_init(&bus->reg_lock);
mutex_init(&bus->cmd_mutex); mutex_init(&bus->cmd_mutex);
mutex_init(&bus->lock);
bus->irq = -1; bus->irq = -1;
return 0; return 0;
} }

View File

@ -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", dev_dbg(bus->dev, "display power %s\n",
enable ? "enable" : "disable"); enable ? "enable" : "disable");
mutex_lock(&bus->lock);
if (enable) if (enable)
set_bit(idx, &bus->display_power_status); set_bit(idx, &bus->display_power_status);
else else
clear_bit(idx, &bus->display_power_status); clear_bit(idx, &bus->display_power_status);
if (!acomp || !acomp->ops) if (!acomp || !acomp->ops)
return; goto unlock;
if (bus->display_power_status) { if (bus->display_power_status) {
if (!bus->display_power_active) { 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; bus->display_power_active = false;
} }
} }
unlock:
mutex_unlock(&bus->lock);
} }
EXPORT_SYMBOL_GPL(snd_hdac_display_power); EXPORT_SYMBOL_GPL(snd_hdac_display_power);