]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/x86/intel/vsec: Fix enable_cnt imbalance on PCIe error recovery
authorLukas Wunner <lukas@wunner.de>
Thu, 14 May 2026 05:40:42 +0000 (07:40 +0200)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Tue, 19 May 2026 14:03:36 +0000 (17:03 +0300)
After a PCIe Uncorrectable Error has been reported by a device with
Intel Vendor Specific Extended Capabilities and has been recovered
through a Secondary Bus Reset, its driver calls intel_vsec_pci_probe()
to rescan and reinitialize VSECs.

intel_vsec_pci_probe() invokes pcim_enable_device() and thereby adds
another devm action which calls pcim_disable_device() on driver unbind.

So once the driver unbinds, pcim_disable_device() will be called as many
times as an Uncorrectable Error occurred, plus one.  This will lead to
an enable_cnt imbalance on driver unbind.

Additionally, since commit dc957ab6aa05 ("platform/x86/intel/vsec: Add
private data for per-device data"), a devm_kzalloc() allocation is
leaked on every Uncorrectable Error.

Avoid by splitting the VSEC rescan out of intel_vsec_pci_probe() into a
separate helper and calling that on PCIe error recovery.

Fixes: 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Cc: stable@vger.kernel.org # v6.0+
Link: https://patch.msgid.link/bd594d09fa866dc51dddc9a447c3b23f9b1402cc.1778736835.git.lukas@wunner.de
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/platform/x86/intel/vsec.c

index 7d5dbc1c1d05a8be698a2262eb6e932c90a085e1..18e4a892bf0f0f75707ce30cddb727416b75c784 100644 (file)
@@ -649,29 +649,13 @@ static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev)
        }
 }
 
-static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int intel_vsec_pci_init(struct pci_dev *pdev)
 {
-       const struct intel_vsec_platform_info *info;
-       struct vsec_priv *priv;
-       int num_caps, ret;
+       struct vsec_priv *priv = pci_get_drvdata(pdev);
+       const struct intel_vsec_platform_info *info = priv->info;
        int run_once = 0;
        bool found_any = false;
-
-       ret = pcim_enable_device(pdev);
-       if (ret)
-               return ret;
-
-       pci_save_state(pdev);
-       info = (const struct intel_vsec_platform_info *)id->driver_data;
-       if (!info)
-               return -EINVAL;
-
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->info = info;
-       pci_set_drvdata(pdev, priv);
+       int num_caps;
 
        num_caps = hweight_long(info->caps);
        while (num_caps--) {
@@ -692,6 +676,31 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
        return 0;
 }
 
+static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       const struct intel_vsec_platform_info *info;
+       struct vsec_priv *priv;
+       int ret;
+
+       ret = pcim_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       pci_save_state(pdev);
+       info = (const struct intel_vsec_platform_info *)id->driver_data;
+       if (!info)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->info = info;
+       pci_set_drvdata(pdev, priv);
+
+       return intel_vsec_pci_init(pdev);
+}
+
 int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info,
                           struct intel_vsec_device *vsec_dev)
 {
@@ -832,7 +841,6 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev)
 {
        struct intel_vsec_device *intel_vsec_dev;
        pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT;
-       const struct pci_device_id *pci_dev_id;
        unsigned long index;
 
        dev_info(&pdev->dev, "Resetting PCI slot\n");
@@ -853,10 +861,8 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev)
                devm_release_action(&pdev->dev, intel_vsec_remove_aux,
                                    &intel_vsec_dev->auxdev);
        }
-       pci_disable_device(pdev);
        pci_restore_state(pdev);
-       pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev);
-       intel_vsec_pci_probe(pdev, pci_dev_id);
+       intel_vsec_pci_init(pdev);
 
 out:
        return status;