From aacd72283650685d86b274156eb99edd1ea852e1 Mon Sep 17 00:00:00 2001 From: Ilkka Koskinen Date: Thu, 5 Dec 2024 06:19:13 +0000 Subject: [PATCH 1/5] perf/dwc_pcie: Fix the event numbers ANBZ: #24704 commit e64c22cc2e6177a5ff59eee394cf4f4dd2f0ed91 upstream. According to Databook, L1 aux is event number 0x08 and TX L0s and RX L0S is 0x09. Fix the event numbers for the two events. Signed-off-by: Ilkka Koskinen Reviewed-by: Shuai Xue Link: https://lore.kernel.org/r/20241205061914.5568-2-ilkka@os.amperecomputing.com Signed-off-by: Will Deacon Signed-off-by: Jing Zhang --- drivers/perf/dwc_pcie_pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 85743a8bdb65..947f70c7f68d 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -200,8 +200,8 @@ static struct attribute *dwc_pcie_pmu_time_event_attrs[] = { DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_1, 0x05), DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_2, 0x06), DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(CFG_RCVRY, 0x07), - DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_RX_L0S, 0x08), - DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_AUX, 0x09), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_AUX, 0x08), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_RX_L0S, 0x09), /* Group #1 */ DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(tx_pcie_tlp_data_payload, 0x20), -- Gitee From 5b7b2ae38b36d528ff45595099ed41c21823933b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Dec 2024 16:29:38 -0600 Subject: [PATCH 2/5] perf/dwc_pcie: Qualify RAS DES VSEC Capability by Vendor, Revision ANBZ: #24704 commit b34d605d120f09303cf8d9e25ad3e17a4997c3a1 upstream. PCI Vendor-Specific (VSEC) Capabilities are defined by each vendor. Devices from different vendors may advertise a VSEC Capability with the DWC RAS DES functionality, but the vendors may assign different VSEC IDs. Search for the DWC RAS DES Capability using the VSEC ID and VSEC Rev chosen by the vendor. This does not fix a current problem because Alibaba, Ampere, and Qualcomm all assigned the same VSEC ID and VSEC Rev for the DWC RAS DES Capability. The potential issue is that we may add support for a device from another vendor, where the vendor has already assigned DWC_PCIE_VSEC_RAS_DES_ID (0x02) for an unrelated VSEC. In that event, dwc_pcie_des_cap() would find the unrelated VSEC and mistakenly assume it was a DWC RAS DES Capability. Signed-off-by: Bjorn Helgaas Reviewed-and-tested-by: Ilkka Koskinen Reviewed-and-tested-by: Shuai Xue Link: https://lore.kernel.org/r/20241209222938.3219364-1-helgaas@kernel.org Signed-off-by: Will Deacon Signed-off-by: Jing Zhang --- drivers/perf/dwc_pcie_pmu.c | 68 ++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 947f70c7f68d..95876389574b 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -20,7 +20,6 @@ #include #include -#define DWC_PCIE_VSEC_RAS_DES_ID 0x02 #define DWC_PCIE_EVENT_CNT_CTL 0x8 /* @@ -101,14 +100,23 @@ struct dwc_pcie_dev_info { struct list_head dev_node; }; -struct dwc_pcie_vendor_id { - int vendor_id; +struct dwc_pcie_pmu_vsec_id { + u16 vendor_id; + u16 vsec_id; + u8 vsec_rev; }; -static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = { - {.vendor_id = PCI_VENDOR_ID_ALIBABA }, - {.vendor_id = PCI_VENDOR_ID_AMPERE }, - {.vendor_id = PCI_VENDOR_ID_QCOM }, +/* + * VSEC IDs are allocated by the vendor, so a given ID may mean different + * things to different vendors. See PCIe r6.0, sec 7.9.5.2. + */ +static const struct dwc_pcie_pmu_vsec_id dwc_pcie_pmu_vsec_ids[] = { + { .vendor_id = PCI_VENDOR_ID_ALIBABA, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_AMPERE, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_QCOM, + .vsec_id = 0x02, .vsec_rev = 0x4 }, {} /* terminator */ }; @@ -520,31 +528,28 @@ static void dwc_pcie_unregister_pmu(void *data) perf_pmu_unregister(&pcie_pmu->pmu); } -static bool dwc_pcie_match_des_cap(struct pci_dev *pdev) +static u16 dwc_pcie_des_cap(struct pci_dev *pdev) { - const struct dwc_pcie_vendor_id *vid; - u16 vsec = 0; + const struct dwc_pcie_pmu_vsec_id *vid; + u16 vsec; u32 val; if (!pci_is_pcie(pdev) || !(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)) - return false; + return 0; - for (vid = dwc_pcie_vendor_ids; vid->vendor_id; vid++) { + for (vid = dwc_pcie_pmu_vsec_ids; vid->vendor_id; vid++) { vsec = pci_find_vsec_capability(pdev, vid->vendor_id, - DWC_PCIE_VSEC_RAS_DES_ID); - if (vsec) - break; + vid->vsec_id); + if (vsec) { + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, + &val); + if (PCI_VNDR_HEADER_REV(val) == vid->vsec_rev) { + pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability RAS DES\n"); + return vsec; + } + } } - if (!vsec) - return false; - - pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); - if (PCI_VNDR_HEADER_REV(val) != 0x04) - return false; - - pci_dbg(pdev, - "Detected PCIe Vendor-Specific Extended Capability RAS DES\n"); - return true; + return 0; } static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info) @@ -588,7 +593,7 @@ static int dwc_pcie_pmu_notifier(struct notifier_block *nb, switch (action) { case BUS_NOTIFY_ADD_DEVICE: - if (!dwc_pcie_match_des_cap(pdev)) + if (!dwc_pcie_des_cap(pdev)) return NOTIFY_DONE; if (dwc_pcie_register_dev(pdev)) return NOTIFY_BAD; @@ -613,13 +618,14 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev) struct pci_dev *pdev = plat_dev->dev.platform_data; struct dwc_pcie_pmu *pcie_pmu; char *name; - u32 sbdf, val; + u32 sbdf; u16 vsec; int ret; - vsec = pci_find_vsec_capability(pdev, pdev->vendor, - DWC_PCIE_VSEC_RAS_DES_ID); - pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); + vsec = dwc_pcie_des_cap(pdev); + if (!vsec) + return -ENODEV; + sbdf = plat_dev->id; name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf); if (!name) @@ -733,7 +739,7 @@ static int __init dwc_pcie_pmu_init(void) int ret; for_each_pci_dev(pdev) { - if (!dwc_pcie_match_des_cap(pdev)) + if (!dwc_pcie_des_cap(pdev)) continue; ret = dwc_pcie_register_dev(pdev); -- Gitee From 7588432b3f1b640e4155fdeb141dbe396c2191dc Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Thu, 20 Feb 2025 20:17:15 +0800 Subject: [PATCH 3/5] perf/dwc_pcie: fix some unreleased resources ANBZ: #24704 commit 6eb1e8ef586ac4a3dcdc20248f9cb45e4ceb141f upstream. Release leaked resources, such as plat_dev and dev_info. Signed-off-by: Yunhui Cui Reviewed-by: Shuai Xue Link: https://lore.kernel.org/r/20250220121716.50324-2-cuiyunhui@bytedance.com Signed-off-by: Will Deacon Signed-off-by: Jing Zhang --- drivers/perf/dwc_pcie_pmu.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 95876389574b..cbd62f2b2cf0 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -573,8 +573,10 @@ static int dwc_pcie_register_dev(struct pci_dev *pdev) return PTR_ERR(plat_dev); dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); - if (!dev_info) + if (!dev_info) { + platform_device_unregister(plat_dev); return -ENOMEM; + } /* Cache platform device to handle pci device hotplug */ dev_info->plat_dev = plat_dev; @@ -733,6 +735,15 @@ static struct platform_driver dwc_pcie_pmu_driver = { .driver = {.name = "dwc_pcie_pmu",}, }; +static void dwc_pcie_cleanup_devices(void) +{ + struct dwc_pcie_dev_info *dev_info, *tmp; + + list_for_each_entry_safe(dev_info, tmp, &dwc_pcie_dev_info_head, dev_node) { + dwc_pcie_unregister_dev(dev_info); + } +} + static int __init dwc_pcie_pmu_init(void) { struct pci_dev *pdev = NULL; @@ -745,7 +756,7 @@ static int __init dwc_pcie_pmu_init(void) ret = dwc_pcie_register_dev(pdev); if (ret) { pci_dev_put(pdev); - return ret; + goto err_cleanup; } } @@ -754,35 +765,35 @@ static int __init dwc_pcie_pmu_init(void) dwc_pcie_pmu_online_cpu, dwc_pcie_pmu_offline_cpu); if (ret < 0) - return ret; + goto err_cleanup; dwc_pcie_pmu_hp_state = ret; ret = platform_driver_register(&dwc_pcie_pmu_driver); if (ret) - goto platform_driver_register_err; + goto err_remove_cpuhp; ret = bus_register_notifier(&pci_bus_type, &dwc_pcie_pmu_nb); if (ret) - goto platform_driver_register_err; + goto err_unregister_driver; notify = true; return 0; -platform_driver_register_err: +err_unregister_driver: + platform_driver_unregister(&dwc_pcie_pmu_driver); +err_remove_cpuhp: cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state); - +err_cleanup: + dwc_pcie_cleanup_devices(); return ret; } static void __exit dwc_pcie_pmu_exit(void) { - struct dwc_pcie_dev_info *dev_info, *tmp; - if (notify) bus_unregister_notifier(&pci_bus_type, &dwc_pcie_pmu_nb); - list_for_each_entry_safe(dev_info, tmp, &dwc_pcie_dev_info_head, dev_node) - dwc_pcie_unregister_dev(dev_info); + dwc_pcie_cleanup_devices(); platform_driver_unregister(&dwc_pcie_pmu_driver); cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state); } -- Gitee From b74e96998abbb0641f60829823d657695e098a71 Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Thu, 20 Feb 2025 20:17:16 +0800 Subject: [PATCH 4/5] perf/dwc_pcie: fix duplicate pci_dev devices ANBZ: #24704 commit 7f35b429802a8065aa61e2a3f567089649f4d98e upstream. During platform_device_register, wrongly using struct device pci_dev as platform_data caused a kmemdup copy of pci_dev. Worse still, accessing the duplicated device leads to list corruption as its mutex content (e.g., list, magic) remains the same as the original. Signed-off-by: Yunhui Cui Reviewed-by: Shuai Xue Link: https://lore.kernel.org/r/20250220121716.50324-3-cuiyunhui@bytedance.com Signed-off-by: Will Deacon Signed-off-by: Jing Zhang --- drivers/perf/dwc_pcie_pmu.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index cbd62f2b2cf0..807d64ffd4c5 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -566,9 +566,7 @@ static int dwc_pcie_register_dev(struct pci_dev *pdev) u32 sbdf; sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn); - plat_dev = platform_device_register_data(NULL, "dwc_pcie_pmu", sbdf, - pdev, sizeof(*pdev)); - + plat_dev = platform_device_register_simple("dwc_pcie_pmu", sbdf, NULL, 0); if (IS_ERR(plat_dev)) return PTR_ERR(plat_dev); @@ -617,18 +615,26 @@ static struct notifier_block dwc_pcie_pmu_nb = { static int dwc_pcie_pmu_probe(struct platform_device *plat_dev) { - struct pci_dev *pdev = plat_dev->dev.platform_data; + struct pci_dev *pdev; struct dwc_pcie_pmu *pcie_pmu; char *name; u32 sbdf; u16 vsec; int ret; + sbdf = plat_dev->id; + pdev = pci_get_domain_bus_and_slot(sbdf >> 16, PCI_BUS_NUM(sbdf & 0xffff), + sbdf & 0xff); + if (!pdev) { + pr_err("No pdev found for the sbdf 0x%x\n", sbdf); + return -ENODEV; + } + vsec = dwc_pcie_des_cap(pdev); if (!vsec) return -ENODEV; - sbdf = plat_dev->id; + pci_dev_put(pdev); name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf); if (!name) return -ENOMEM; @@ -643,7 +649,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev) pcie_pmu->on_cpu = -1; pcie_pmu->pmu = (struct pmu){ .name = name, - .parent = &pdev->dev, + .parent = &plat_dev->dev, .module = THIS_MODULE, .attr_groups = dwc_pcie_attr_groups, .capabilities = PERF_PMU_CAP_NO_EXCLUDE, -- Gitee From 1159dd4d95bba0a38289a0b237f254570af58dfe Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 21 Feb 2025 18:45:44 +0530 Subject: [PATCH 5/5] perf/dwc_pcie: Move common DWC struct definitions to 'pcie-dwc.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #24704 commit 5d2b978ff9b13b1286c6e1c753b331f4bd98d7ff upstream. Move the common DWC struct definitions, which are shared across all the DesginWare PCIe IPs, to a new header file called 'pcie-dwc.h', so that other users e.g., debugfs, perf and sysfs can make use of them. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Shradha Todi Reviewed-by: Shuai Xue Reviewed-by: Fan Ni Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-2-shradha.t@samsung.com [kwilczynski: commit log, tidy up the new header file] Signed-off-by: Krzysztof WilczyƄski Signed-off-by: Jing Zhang --- MAINTAINERS | 1 + drivers/perf/dwc_pcie_pmu.c | 25 +++---------------------- include/linux/pcie-dwc.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 include/linux/pcie-dwc.h diff --git a/MAINTAINERS b/MAINTAINERS index 88a94a62ab07..fa3a64a369c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13594,6 +13594,7 @@ L: linux-pci@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pci/designware-pcie.txt F: drivers/pci/controller/dwc/*designware* +F: include/linux/pcie-dwc.h PCI DRIVER FOR TI DRA7XX/J721E M: Kishon Vijay Abraham I diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 807d64ffd4c5..af286d8a28e2 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -100,26 +101,6 @@ struct dwc_pcie_dev_info { struct list_head dev_node; }; -struct dwc_pcie_pmu_vsec_id { - u16 vendor_id; - u16 vsec_id; - u8 vsec_rev; -}; - -/* - * VSEC IDs are allocated by the vendor, so a given ID may mean different - * things to different vendors. See PCIe r6.0, sec 7.9.5.2. - */ -static const struct dwc_pcie_pmu_vsec_id dwc_pcie_pmu_vsec_ids[] = { - { .vendor_id = PCI_VENDOR_ID_ALIBABA, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - { .vendor_id = PCI_VENDOR_ID_AMPERE, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - { .vendor_id = PCI_VENDOR_ID_QCOM, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - {} /* terminator */ -}; - static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -530,14 +511,14 @@ static void dwc_pcie_unregister_pmu(void *data) static u16 dwc_pcie_des_cap(struct pci_dev *pdev) { - const struct dwc_pcie_pmu_vsec_id *vid; + const struct dwc_pcie_vsec_id *vid; u16 vsec; u32 val; if (!pci_is_pcie(pdev) || !(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)) return 0; - for (vid = dwc_pcie_pmu_vsec_ids; vid->vendor_id; vid++) { + for (vid = dwc_pcie_rasdes_vsec_ids; vid->vendor_id; vid++) { vsec = pci_find_vsec_capability(pdev, vid->vendor_id, vid->vsec_id); if (vsec) { diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h new file mode 100644 index 000000000000..6e4e1307ad6e --- /dev/null +++ b/include/linux/pcie-dwc.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021-2023 Alibaba Inc. + * Copyright (C) 2025 Linaro Ltd. + * + * Author: Manivannan Sadhasivam + */ + +#ifndef LINUX_PCIE_DWC_H +#define LINUX_PCIE_DWC_H + +#include + +struct dwc_pcie_vsec_id { + u16 vendor_id; + u16 vsec_id; + u8 vsec_rev; +}; + +/* + * VSEC IDs are allocated by the vendor, so a given ID may mean different + * things to different vendors. See PCIe r6.0, sec 7.9.5.2. + */ +static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = { + { .vendor_id = PCI_VENDOR_ID_ALIBABA, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_AMPERE, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_QCOM, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + {} +}; + +#endif /* LINUX_PCIE_DWC_H */ -- Gitee