platform/x86/intel/vsec: Fix enable_cnt imbalance on PCIe error recovery

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 dc957ab6aa ("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: 936874b77d ("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>
This commit is contained in:
Lukas Wunner 2026-05-14 07:40:42 +02:00 committed by Ilpo Järvinen
parent 26cbe119f9
commit 348ccc754d
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31

View 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;