[alsa-devel] [PATCH 4/4 V2] ALSA: hda - Continue probe in work context to avoid request_module deadlock

Wang Xingchao xingchao.wang at linux.intel.com
Thu May 23 03:51:07 CEST 2013


There's deadlock when request_module(i915) in azx_probe.
It looks like:
device_lock(audio pci device) -> azx_probe -> module_request
(or symbol_request) -> modprobe (userspace) -> i915 init ->
drm_pci_init -> pci_register_driver -> bus_add_driver -> driver_attach ->
which in turn tries all locks on pci bus, and when it tries the one on the
audio device, it will deadlock.

This patch introduce a work to store remaining probe stuff, and let
request_module run in safe work context.

Signed-off-by: Wang Xingchao <xingchao.wang at linux.intel.com>
---
 sound/pci/hda/hda_i915.c  |  13 ++++--
 sound/pci/hda/hda_intel.c | 105 +++++++++++++++++++++++++++-------------------
 2 files changed, 71 insertions(+), 47 deletions(-)

diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
index 76c13d5..7547b20 100644
--- a/sound/pci/hda/hda_i915.c
+++ b/sound/pci/hda/hda_i915.c
@@ -42,13 +42,18 @@ int hda_i915_init(void)
 {
 	int err = 0;
 
-	get_power = symbol_request(i915_request_power_well);
+	get_power = symbol_get(i915_request_power_well);
 	if (!get_power) {
-		snd_printk(KERN_WARNING "hda-i915: get_power symbol get fail\n");
-		return -ENODEV;
+		request_module("i915");
+		get_power = symbol_get(i915_request_power_well);
+		if (!get_power) {
+			snd_printk(KERN_WARNING "hda-i915: get_power symbol get fail\n");
+			return -ENODEV;
+		}
+		snd_printdd("hda-i915: get_power symbol get successful\n");
 	}
 
-	put_power = symbol_request(i915_release_power_well);
+	put_power = symbol_get(i915_release_power_well);
 	if (!put_power) {
 		symbol_put(i915_request_power_well);
 		get_power = NULL;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index f20a88c..1bc7c3b 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -76,6 +76,7 @@ static int probe_only[SNDRV_CARDS];
 static int jackpoll_ms[SNDRV_CARDS];
 static bool single_cmd;
 static int enable_msi = -1;
+static int dev;
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
 static char *patch[SNDRV_CARDS];
 #endif
@@ -542,6 +543,8 @@ struct azx {
 	/* for pending irqs */
 	struct work_struct irq_pending_work;
 
+	struct delayed_work probe_work;
+
 	/* reboot notifier (for mysterious hangup problem at power-down) */
 	struct notifier_block reboot_notifier;
 
@@ -3670,58 +3673,22 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
 }
 #endif
 
-static int azx_probe(struct pci_dev *pci,
-		     const struct pci_device_id *pci_id)
+static void azx_probe_work(struct work_struct *work)
 {
-	static int dev;
-	struct snd_card *card;
-	struct azx *chip;
+	struct azx *chip =
+		container_of(work, struct azx, probe_work.work);
+	struct snd_card *card = chip->card;
+	struct pci_dev *pci = chip->pci;
 	bool probe_now;
 	int err;
 
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-	if (err < 0) {
-		snd_printk(KERN_ERR "hda-intel: Error creating card!\n");
-		return err;
-	}
-
-	snd_card_set_dev(card, &pci->dev);
-
-	err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
-	if (err < 0)
-		goto out_free;
-	card->private_data = chip;
-
-	pci_set_drvdata(pci, card);
-
-	err = register_vga_switcheroo(chip);
-	if (err < 0) {
-		snd_printk(KERN_ERR SFX
-			   "%s: Error registering VGA-switcheroo client\n", pci_name(pci));
-		goto out_free;
-	}
-
-	if (check_hdmi_disabled(pci)) {
-		snd_printk(KERN_INFO SFX "%s: VGA controller is disabled\n",
-			   pci_name(pci));
-		snd_printk(KERN_INFO SFX "%s: Delaying initialization\n", pci_name(pci));
-		chip->disabled = true;
-	}
-
 	/* Request power well for Haswell HDA controller and codec */
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 #ifdef CONFIG_SND_HDA_I915
 		err = hda_i915_init();
 		if (err < 0) {
 			snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
-			return err;
+			goto out_free;
 		}
 		hda_display_power(true);
 #else
@@ -3760,7 +3727,7 @@ static int azx_probe(struct pci_dev *pci,
 
 	dev++;
 	complete_all(&chip->probe_wait);
-	return 0;
+	return;
 out_free_power:
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 		hda_display_power(false);
@@ -3769,6 +3736,58 @@ out_free_power:
 out_free:
 	snd_card_free(card);
 	pci_set_drvdata(pci, NULL);
+}
+
+static int azx_probe(struct pci_dev *pci,
+		     const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct azx *chip;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+	if (err < 0) {
+		snd_printk(KERN_ERR "hda-intel: Error creating card!\n");
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
+	if (err < 0)
+		goto out_free;
+	card->private_data = chip;
+
+	pci_set_drvdata(pci, card);
+
+	err = register_vga_switcheroo(chip);
+	if (err < 0) {
+		snd_printk(KERN_ERR SFX
+			   "%s: Error registering VGA-switcheroo client\n", pci_name(pci));
+		goto out_free;
+	}
+
+	if (check_hdmi_disabled(pci)) {
+		snd_printk(KERN_INFO SFX "%s: VGA controller is disabled\n",
+			   pci_name(pci));
+		snd_printk(KERN_INFO SFX "%s: Delaying initialization\n", pci_name(pci));
+		chip->disabled = true;
+	}
+
+	/* continue probing in work context as may trigger request module */
+	INIT_DELAYED_WORK(&chip->probe_work, azx_probe_work);
+	schedule_delayed_work(&chip->probe_work, 0);
+	return 0;
+out_free:
+	snd_card_free(card);
+	pci_set_drvdata(pci, NULL);
 	return err;
 }
 
-- 
1.8.1.2



More information about the Alsa-devel mailing list