| From 02928be5c9c84191e1856947e3c5e4478ecb2bce Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Mon, 13 Apr 2020 10:20:31 +0200 |
| Subject: [PATCH] ALSA: hda: Release resources at error in delayed probe |
| |
| commit 2393e7555b531a534152ffe7bfd1862cacedaacb upstream. |
| |
| snd-hda-intel driver handles the most of its probe task in the delayed |
| work (either via workqueue or via firmware loader). When an error |
| happens in the later delayed probe, we can't deregister the device |
| itself because the probe callback already returned success and the |
| device was bound. So, for now, we set hda->init_failed flag and make |
| the rest untouched until the device gets really unbound. |
| However, this leaves the device up running, keeping the resources |
| without any use that prevents other operations. |
| |
| In this patch, we release the resources at first when a probe error |
| happens in the delayed probe stage, but keeps the top-level object, so |
| that the PM and other ops can still refer to the object itself. |
| |
| Also for simplicity, snd_hda_intel object is allocated via devm, so |
| that we can get rid of the explicit kfree calls. |
| |
| BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=207043 |
| Link: https://lore.kernel.org/r/20200413082034.25166-4-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c |
| index c5a2e0646f4e..477153bd687f 100644 |
| --- a/sound/pci/hda/hda_intel.c |
| +++ b/sound/pci/hda/hda_intel.c |
| @@ -1200,10 +1200,8 @@ static void azx_vs_set_state(struct pci_dev *pci, |
| if (!disabled) { |
| dev_info(chip->card->dev, |
| "Start delayed initialization\n"); |
| - if (azx_probe_continue(chip) < 0) { |
| + if (azx_probe_continue(chip) < 0) |
| dev_err(chip->card->dev, "initialization error\n"); |
| - hda->init_failed = true; |
| - } |
| } |
| } else { |
| dev_info(chip->card->dev, "%s via vga_switcheroo\n", |
| @@ -1331,12 +1329,15 @@ static int register_vga_switcheroo(struct azx *chip) |
| /* |
| * destructor |
| */ |
| -static int azx_free(struct azx *chip) |
| +static void azx_free(struct azx *chip) |
| { |
| struct pci_dev *pci = chip->pci; |
| struct hda_intel *hda = container_of(chip, struct hda_intel, chip); |
| struct hdac_bus *bus = azx_bus(chip); |
| |
| + if (hda->freed) |
| + return; |
| + |
| if (azx_has_pm_runtime(chip) && chip->running) |
| pm_runtime_get_noresume(&pci->dev); |
| chip->running = 0; |
| @@ -1380,9 +1381,8 @@ static int azx_free(struct azx *chip) |
| |
| if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) |
| snd_hdac_i915_exit(bus); |
| - kfree(hda); |
| |
| - return 0; |
| + hda->freed = 1; |
| } |
| |
| static int azx_dev_disconnect(struct snd_device *device) |
| @@ -1398,7 +1398,8 @@ static int azx_dev_disconnect(struct snd_device *device) |
| |
| static int azx_dev_free(struct snd_device *device) |
| { |
| - return azx_free(device->device_data); |
| + azx_free(device->device_data); |
| + return 0; |
| } |
| |
| #ifdef SUPPORT_VGA_SWITCHEROO |
| @@ -1713,7 +1714,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, |
| if (err < 0) |
| return err; |
| |
| - hda = kzalloc(sizeof(*hda), GFP_KERNEL); |
| + hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL); |
| if (!hda) { |
| pci_disable_device(pci); |
| return -ENOMEM; |
| @@ -1758,7 +1759,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, |
| |
| err = azx_bus_init(chip, model[dev], &pci_hda_io_ops); |
| if (err < 0) { |
| - kfree(hda); |
| pci_disable_device(pci); |
| return err; |
| } |
| @@ -2331,13 +2331,16 @@ static int azx_probe_continue(struct azx *chip) |
| pm_runtime_put_autosuspend(&pci->dev); |
| |
| out_free: |
| - if (err < 0 || !hda->need_i915_power) |
| + if (err < 0) { |
| + azx_free(chip); |
| + return err; |
| + } |
| + |
| + if (!hda->need_i915_power) |
| display_power(chip, false); |
| - if (err < 0) |
| - hda->init_failed = 1; |
| complete_all(&hda->probe_wait); |
| to_hda_bus(bus)->bus_probing = 0; |
| - return err; |
| + return 0; |
| } |
| |
| static void azx_remove(struct pci_dev *pci) |
| diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h |
| index 1468865e0342..9302c9e67950 100644 |
| --- a/sound/pci/hda/hda_intel.h |
| +++ b/sound/pci/hda/hda_intel.h |
| @@ -28,6 +28,7 @@ struct hda_intel { |
| unsigned int need_eld_notify_link:1; |
| unsigned int vga_switcheroo_registered:1; |
| unsigned int init_failed:1; /* delayed init failed */ |
| + unsigned int freed:1; /* resources already released */ |
| |
| bool need_i915_power:1; /* the hda controller needs i915 power */ |
| }; |
| -- |
| 2.7.4 |
| |