Unverified Commit 348ccc75 authored by Lukas Wunner's avatar Lukas Wunner Committed by Ilpo Järvinen
Browse files

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 dc957ab6 ("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: 936874b7 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT")
Signed-off-by: default avatarLukas Wunner <lukas@wunner.de>
Cc: stable@vger.kernel.org  # v6.0+
Link: https://patch.msgid.link/bd594d09fa866dc51dddc9a447c3b23f9b1402cc.1778736835.git.lukas@wunner.de


Reviewed-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
parent 26cbe119
Loading
Loading
Loading
Loading
+30 −24
Original line number Diff line number Diff line
@@ -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;