* [PATCH v2 0/3] New vendor support and optimizations for DWC PCIe PMU
@ 2026-06-29 9:27 Yicong Yang
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Yicong Yang @ 2026-06-29 9:27 UTC (permalink / raw)
To: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, yang.yicong, douyufan
This patchset mainly enable the DWC PCIe PMU on our platform (Patch 1/3).
Besides do some optimization and refactor:
- Patch 2/3 extend the monitoring limit on platforms with narrowed counter width (< 64 bit)
- Patch 3/3 switch the driver to use the faux device interface
Change since v1:
- always enable hrtimer for updating narrowed counters on affected platform
- refine the commints and fix styles per Jonthan, thanks.
Link: https://lore.kernel.org/all/20260615063459.25361-1-yang.yicong@picoheart.com/
Yicong Yang (2):
perf/dwc_pcie: Add support for Picoheart vendor devices
perf/dwc_pcie: Convert to faux device interface
Yufan Dou (1):
perf/dwc_pcie: Support narrowed time-based counter for long time
monitoring
drivers/perf/dwc_pcie_pmu.c | 170 ++++++++++++++++++++++++------------
include/linux/pci_ids.h | 2 +
include/linux/pcie-dwc.h | 2 +
3 files changed, 117 insertions(+), 57 deletions(-)
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices
2026-06-29 9:27 [PATCH v2 0/3] New vendor support and optimizations for DWC PCIe PMU Yicong Yang
@ 2026-06-29 9:27 ` Yicong Yang
2026-06-29 9:40 ` sashiko-bot
` (3 more replies)
2026-06-29 9:27 ` [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring Yicong Yang
2026-06-29 9:27 ` [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface Yicong Yang
2 siblings, 4 replies; 12+ messages in thread
From: Yicong Yang @ 2026-06-29 9:27 UTC (permalink / raw)
To: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, yang.yicong, douyufan
Add PCI_VENDOR_ID_PICOHEART in pci_ids.h. Update the DWC PCIe
vendor table with Picoheart PCIe vendorid to enable the PCIe
PMU support.
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
---
include/linux/pci_ids.h | 2 ++
include/linux/pcie-dwc.h | 2 ++
2 files changed, 4 insertions(+)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1c9d40e09107..2c17239aacea 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2640,6 +2640,8 @@
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
+#define PCI_VENDOR_ID_PICOHEART 0x20fa
+
#define PCI_VENDOR_ID_HINT 0x3388
#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
index 8ff778e7aec0..b0ccb2ad74db 100644
--- a/include/linux/pcie-dwc.h
+++ b/include/linux/pcie-dwc.h
@@ -32,6 +32,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_SAMSUNG,
.vsec_id = 0x02, .vsec_rev = 0x4 },
+ { .vendor_id = PCI_VENDOR_ID_PICOHEART,
+ .vsec_id = 0x02, .vsec_rev = 0x4 },
{}
};
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring
2026-06-29 9:27 [PATCH v2 0/3] New vendor support and optimizations for DWC PCIe PMU Yicong Yang
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
@ 2026-06-29 9:27 ` Yicong Yang
2026-06-29 9:42 ` sashiko-bot
2026-06-30 8:50 ` Shuai Xue
2026-06-29 9:27 ` [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface Yicong Yang
2 siblings, 2 replies; 12+ messages in thread
From: Yicong Yang @ 2026-06-29 9:27 UTC (permalink / raw)
To: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, yang.yicong, douyufan
From: Yufan Dou <douyufan@picoheart.com>
The DWC PCIe Time-Based Analysis Data Register (the counter for time-based
events) is architected as 64-bit, but some hardware implementations do not
implement the full width. On these implementations the counter stops after
reaching its implemented width. This will limit the usage for short time
monitoring only. The counter will only cover ~15s for monitoring RX TLP
payloads on our platform.
Add an optional hrtimer that fires every 2 seconds. It'll take the role
as the counter overflow interrupt to read-update-reset the counter and
event counts to break the limits of the narrow counters. It'll only
apply on timer-based counter. The 2 seconds update period is the half
of the maximum counting period (4s) of the time-based counter under
period counting mode of the hardware.
Because fully-implemented 64-bit counters do not need this workaround,
enable this hrtimer on the platforms known to have narrowed counter.
Before this patch, when counting fio for 10m the counts is incorrect:
root@localhost:/tmp# perf stat -e dwc_rootport_20000/rx_pcie_tlp_data_payload/ -- fio --runtime=10m fio_job.config
[...]
Run status group 0 (all jobs):
READ: bw=5594MiB/s (5865MB/s), 5594MiB/s-5594MiB/s (5865MB/s-5865MB/s), io=3278GiB (3519GB), run=600010-600010msec
[...]
Performance counter stats for 'system wide':
137,438,953,456 dwc_rootport_20000/rx_pcie_tlp_data_payload/
After this patch the counts is as expected:
root@localhost:/tmp# perf stat -e dwc_rootport_20000/rx_pcie_tlp_data_payload/ -- fio --runtime=10m fio_job.config
[...]
Run status group 0 (all jobs):
READ: bw=5632MiB/s (5905MB/s), 5632MiB/s-5632MiB/s (5905MB/s-5905MB/s), io=3300GiB (3543GB), run=600013-600013msec
[...]
Performance counter stats for 'system wide':
3,543,850,268,576 dwc_rootport_20000/rx_pcie_tlp_data_payload/
Signed-off-by: Yufan Dou <douyufan@picoheart.com>
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
---
drivers/perf/dwc_pcie_pmu.c | 69 ++++++++++++++++++++++++++++++++++---
1 file changed, 65 insertions(+), 4 deletions(-)
diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
index 5385401fa9cf..7ec8302d4090 100644
--- a/drivers/perf/dwc_pcie_pmu.c
+++ b/drivers/perf/dwc_pcie_pmu.c
@@ -11,6 +11,7 @@
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/hrtimer.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pcie-dwc.h>
@@ -83,6 +84,7 @@ enum dwc_pcie_event_type {
#define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0)
#define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0)
+#define DWC_PCIE_PMU_TIMER_PERIOD_NS (2 * NSEC_PER_SEC)
struct dwc_pcie_pmu {
struct pmu pmu;
@@ -93,6 +95,8 @@ struct dwc_pcie_pmu {
/* Groups #6 and #7 */
DECLARE_BITMAP(lane_events, 2 * DWC_PCIE_LANE_MAX_EVENTS_PER_GROUP);
struct perf_event *time_based_event;
+ bool timer_enable;
+ struct hrtimer hrtimer;
struct hlist_node cpuhp_node;
int on_cpu;
@@ -354,6 +358,26 @@ static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event)
return val;
}
+static void dwc_pcie_pmu_reset_time_based_counter(struct perf_event *event)
+{
+ struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ u64 prev;
+
+ dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
+
+ /*
+ * The hardware counter is reset to zero when disabled. Synchronize
+ * prev_count so that the next event_update() computes the correct
+ * delta against the new counter baseline.
+ */
+ do {
+ prev = local64_read(&hwc->prev_count);
+ } while (local64_cmpxchg(&hwc->prev_count, prev, 0) != prev);
+
+ dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
+}
+
static void dwc_pcie_pmu_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -429,6 +453,26 @@ static int dwc_pcie_pmu_validate_group(struct perf_event *event)
return 0;
}
+static enum hrtimer_restart dwc_pcie_pmu_hrtimer_callback(struct hrtimer *hrtimer)
+{
+ struct dwc_pcie_pmu *pcie_pmu = container_of(hrtimer, struct dwc_pcie_pmu, hrtimer);
+ struct perf_event *event = pcie_pmu->time_based_event;
+ struct hw_perf_event *hwc;
+
+ if (!event)
+ return HRTIMER_NORESTART;
+
+ hwc = &event->hw;
+ if (hwc->state & PERF_HES_STOPPED)
+ return HRTIMER_NORESTART;
+
+ dwc_pcie_pmu_event_update(event);
+ dwc_pcie_pmu_reset_time_based_counter(event);
+ hrtimer_forward_now(hrtimer, ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS));
+
+ return HRTIMER_RESTART;
+}
+
static int dwc_pcie_pmu_event_init(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
@@ -478,10 +522,15 @@ static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags)
hwc->state = 0;
local64_set(&hwc->prev_count, 0);
- if (type == DWC_PCIE_LANE_EVENT)
+ if (type == DWC_PCIE_LANE_EVENT) {
dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, true);
- else if (type == DWC_PCIE_TIME_BASE_EVENT)
+ } else if (type == DWC_PCIE_TIME_BASE_EVENT) {
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
+ if (pcie_pmu->timer_enable)
+ hrtimer_start(&pcie_pmu->hrtimer,
+ ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS),
+ HRTIMER_MODE_REL_PINNED_HARD);
+ }
}
static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
@@ -495,10 +544,12 @@ static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
dwc_pcie_pmu_event_update(event);
- if (type == DWC_PCIE_LANE_EVENT)
+ if (type == DWC_PCIE_LANE_EVENT) {
dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, false);
- else if (type == DWC_PCIE_TIME_BASE_EVENT)
+ } else if (type == DWC_PCIE_TIME_BASE_EVENT) {
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
+ hrtimer_cancel(&pcie_pmu->hrtimer);
+ }
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
@@ -726,6 +777,16 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
pcie_pmu->ras_des_offset = vsec;
pcie_pmu->nr_lanes = pcie_get_width_cap(pdev);
pcie_pmu->on_cpu = -1;
+ hrtimer_setup(&pcie_pmu->hrtimer, dwc_pcie_pmu_hrtimer_callback,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED_HARD);
+
+ /*
+ * Use timer for updating time-based counts on platforms known
+ * to have narrowed counter.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_PICOHEART)
+ pcie_pmu->timer_enable = true;
+
pcie_pmu->pmu = (struct pmu){
.name = name,
.parent = &plat_dev->dev,
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface
2026-06-29 9:27 [PATCH v2 0/3] New vendor support and optimizations for DWC PCIe PMU Yicong Yang
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
2026-06-29 9:27 ` [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring Yicong Yang
@ 2026-06-29 9:27 ` Yicong Yang
2026-06-29 9:40 ` sashiko-bot
2026-06-30 9:19 ` Shuai Xue
2 siblings, 2 replies; 12+ messages in thread
From: Yicong Yang @ 2026-06-29 9:27 UTC (permalink / raw)
To: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, yang.yicong, douyufan
The DWC PCIe PMU makes use of the platform device interface but
is not the real device, it's actually the RAS DES capability of
the root port. It's more appropriate to use the lightweight
faux framework to abstract this, it'll be more simple and no
need for the complete device and drivers model. So convert to
the faux device interface.
Move the cpuhp state registration prior to faux device creation
since the probe depends on this and will run by the faux device
creation.
No functional changes intended.
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
---
drivers/perf/dwc_pcie_pmu.c | 101 +++++++++++++++++-------------------
1 file changed, 48 insertions(+), 53 deletions(-)
diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
index 7ec8302d4090..3a9965b5abf4 100644
--- a/drivers/perf/dwc_pcie_pmu.c
+++ b/drivers/perf/dwc_pcie_pmu.c
@@ -10,6 +10,7 @@
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/device.h>
+#include <linux/device/faux.h>
#include <linux/errno.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
@@ -17,7 +18,6 @@
#include <linux/pcie-dwc.h>
#include <linux/perf_event.h>
#include <linux/pci.h>
-#include <linux/platform_device.h>
#include <linux/smp.h>
#include <linux/sysfs.h>
#include <linux/types.h>
@@ -110,9 +110,10 @@ static struct list_head dwc_pcie_dev_info_head =
static bool notify;
struct dwc_pcie_dev_info {
- struct platform_device *plat_dev;
+ struct faux_device *fdev;
struct pci_dev *pdev;
struct list_head dev_node;
+ char *name;
};
static ssize_t cpumask_show(struct device *dev,
@@ -658,6 +659,12 @@ static void dwc_pcie_unregister_pmu(void *data)
perf_pmu_unregister(&pcie_pmu->pmu);
}
+static int dwc_pcie_pmu_probe(struct faux_device *fdev);
+
+static struct faux_device_ops dwc_pcie_faux_ops = {
+ .probe = dwc_pcie_pmu_probe,
+};
+
static u16 dwc_pcie_des_cap(struct pci_dev *pdev)
{
const struct dwc_pcie_vsec_id *vid;
@@ -684,31 +691,41 @@ static u16 dwc_pcie_des_cap(struct pci_dev *pdev)
static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info)
{
- platform_device_unregister(dev_info->plat_dev);
+ faux_device_destroy(dev_info->fdev);
list_del(&dev_info->dev_node);
+ kfree(dev_info->name);
kfree(dev_info);
}
static int dwc_pcie_register_dev(struct pci_dev *pdev)
{
- struct platform_device *plat_dev;
struct dwc_pcie_dev_info *dev_info;
+ struct faux_device *fdev;
+ char *name;
u32 sbdf;
sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
- plat_dev = platform_device_register_simple("dwc_pcie_pmu", sbdf, NULL, 0);
- if (IS_ERR(plat_dev))
- return PTR_ERR(plat_dev);
+ name = kasprintf(GFP_KERNEL, "dwc_pcie_pmu_%x", sbdf);
+ if (!name)
+ return -ENOMEM;
+
+ fdev = faux_device_create(name, &pdev->dev, &dwc_pcie_faux_ops);
+ if (!fdev) {
+ kfree(name);
+ return -ENODEV;
+ }
dev_info = kzalloc_obj(*dev_info);
if (!dev_info) {
- platform_device_unregister(plat_dev);
+ faux_device_destroy(fdev);
+ kfree(name);
return -ENOMEM;
}
- /* Cache platform device to handle pci device hotplug */
- dev_info->plat_dev = plat_dev;
+ /* Cache faux device to handle pci device hotplug */
+ dev_info->fdev = fdev;
dev_info->pdev = pdev;
+ dev_info->name = name;
list_add(&dev_info->dev_node, &dwc_pcie_dev_info_head);
return 0;
@@ -743,33 +760,25 @@ static struct notifier_block dwc_pcie_pmu_nb = {
.notifier_call = dwc_pcie_pmu_notifier,
};
-static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
+static int dwc_pcie_pmu_probe(struct faux_device *fdev)
{
- struct pci_dev *pdev;
+ struct pci_dev *pdev = to_pci_dev(fdev->dev.parent);
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;
- }
+ sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
+ name = devm_kasprintf(&fdev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf);
+ if (!name)
+ return -ENOMEM;
vsec = dwc_pcie_des_cap(pdev);
if (!vsec)
return -ENODEV;
- pci_dev_put(pdev);
- name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf);
- if (!name)
- return -ENOMEM;
-
- pcie_pmu = devm_kzalloc(&plat_dev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
+ pcie_pmu = devm_kzalloc(&fdev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
if (!pcie_pmu)
return -ENOMEM;
@@ -789,7 +798,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
pcie_pmu->pmu = (struct pmu){
.name = name,
- .parent = &plat_dev->dev,
+ .parent = &fdev->dev,
.module = THIS_MODULE,
.attr_groups = dwc_pcie_attr_groups,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
@@ -811,7 +820,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
}
/* Unwind when platform driver removes */
- ret = devm_add_action_or_reset(&plat_dev->dev,
+ ret = devm_add_action_or_reset(&fdev->dev,
dwc_pcie_pmu_remove_cpuhp_instance,
&pcie_pmu->cpuhp_node);
if (ret)
@@ -822,7 +831,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
pci_err(pdev, "Error %d registering PMU @%x\n", ret, sbdf);
return ret;
}
- ret = devm_add_action_or_reset(&plat_dev->dev, dwc_pcie_unregister_pmu,
+ ret = devm_add_action_or_reset(&fdev->dev, dwc_pcie_unregister_pmu,
pcie_pmu);
if (ret)
return ret;
@@ -874,11 +883,6 @@ static int dwc_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_n
return 0;
}
-static struct platform_driver dwc_pcie_pmu_driver = {
- .probe = dwc_pcie_pmu_probe,
- .driver = {.name = "dwc_pcie_pmu",},
-};
-
static void dwc_pcie_cleanup_devices(void)
{
struct dwc_pcie_dev_info *dev_info, *tmp;
@@ -893,6 +897,15 @@ static int __init dwc_pcie_pmu_init(void)
struct pci_dev *pdev = NULL;
int ret;
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
+ "perf/dwc_pcie_pmu:online",
+ dwc_pcie_pmu_online_cpu,
+ dwc_pcie_pmu_offline_cpu);
+ if (ret < 0)
+ return ret;
+
+ dwc_pcie_pmu_hp_state = ret;
+
for_each_pci_dev(pdev) {
if (!dwc_pcie_des_cap(pdev))
continue;
@@ -904,32 +917,15 @@ static int __init dwc_pcie_pmu_init(void)
}
}
- ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
- "perf/dwc_pcie_pmu:online",
- dwc_pcie_pmu_online_cpu,
- dwc_pcie_pmu_offline_cpu);
- if (ret < 0)
- goto err_cleanup;
-
- dwc_pcie_pmu_hp_state = ret;
-
- ret = platform_driver_register(&dwc_pcie_pmu_driver);
- if (ret)
- goto err_remove_cpuhp;
-
ret = bus_register_notifier(&pci_bus_type, &dwc_pcie_pmu_nb);
if (ret)
- goto err_unregister_driver;
+ goto err_cleanup;
notify = true;
return 0;
-
-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();
+ cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state);
return ret;
}
@@ -938,7 +934,6 @@ static void __exit dwc_pcie_pmu_exit(void)
if (notify)
bus_unregister_notifier(&pci_bus_type, &dwc_pcie_pmu_nb);
dwc_pcie_cleanup_devices();
- platform_driver_unregister(&dwc_pcie_pmu_driver);
cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state);
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface
2026-06-29 9:27 ` [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface Yicong Yang
@ 2026-06-29 9:40 ` sashiko-bot
2026-06-30 9:19 ` Shuai Xue
1 sibling, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-29 9:40 UTC (permalink / raw)
To: Yicong Yang; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Lockless modifications to `dwc_pcie_dev_info_head` corrupt the global list during concurrent PCI hotplug events.
- [High] Returning NOTIFY_BAD halts the notifier chain and breaks subsystems.
- [High] Premature CPU hotplug registration crashes the kernel via a NULL pointer dereference.
--
commit 699b6522ae4c9d70c5bdeb799d79993149c02386
Author: Yicong Yang <yang.yicong@picoheart.com>
perf/dwc_pcie: Convert to faux device interface
The DWC PCIe PMU makes use of the platform device interface but
is not the real device, it's actually the RAS DES capability of
the root port. It's more appropriate to use the lightweight
faux framework to abstract this, it'll be more simple and no
need for the complete device and drivers model. So convert to
the faux device interface.
> diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
> index 7ec8302d4090b..3a9965b5abf4b 100644
> --- a/drivers/perf/dwc_pcie_pmu.c
> +++ b/drivers/perf/dwc_pcie_pmu.c
> @@ -684,31 +691,41 @@ static u16 dwc_pcie_des_cap(struct pci_dev *pdev)
>
> static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info)
> {
> - platform_device_unregister(dev_info->plat_dev);
> + faux_device_destroy(dev_info->fdev);
> list_del(&dev_info->dev_node);
> + kfree(dev_info->name);
> kfree(dev_info);
> }
>
> static int dwc_pcie_register_dev(struct pci_dev *pdev)
> {
> - struct platform_device *plat_dev;
> struct dwc_pcie_dev_info *dev_info;
> + struct faux_device *fdev;
> + char *name;
> u32 sbdf;
>
> sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
> - plat_dev = platform_device_register_simple("dwc_pcie_pmu", sbdf, NULL, 0);
> - if (IS_ERR(plat_dev))
> - return PTR_ERR(plat_dev);
> + name = kasprintf(GFP_KERNEL, "dwc_pcie_pmu_%x", sbdf);
> + if (!name)
> + return -ENOMEM;
> +
> + fdev = faux_device_create(name, &pdev->dev, &dwc_pcie_faux_ops);
> + if (!fdev) {
> + kfree(name);
> + return -ENODEV;
> + }
>
> dev_info = kzalloc_obj(*dev_info);
> if (!dev_info) {
> - platform_device_unregister(plat_dev);
> + faux_device_destroy(fdev);
> + kfree(name);
> return -ENOMEM;
> }
>
> - /* Cache platform device to handle pci device hotplug */
> - dev_info->plat_dev = plat_dev;
> + /* Cache faux device to handle pci device hotplug */
> + dev_info->fdev = fdev;
> dev_info->pdev = pdev;
> + dev_info->name = name;
> list_add(&dev_info->dev_node, &dwc_pcie_dev_info_head);
[Severity: High]
This isn't a bug introduced by this patch, but does this list modification
need synchronization?
Concurrent PCI hotplug events can invoke the bus notifier, which calls
dwc_pcie_register_dev() or dwc_pcie_unregister_dev(). This results in
list_add() or list_del() on dwc_pcie_dev_info_head without any locking,
which can cause list corruption and lead to an infinite loop or kernel panic.
[Severity: High]
This is a pre-existing issue, but if dwc_pcie_register_dev() fails
during the BUS_NOTIFY_ADD_DEVICE event:
drivers/perf/dwc_pcie_pmu.c:dwc_pcie_pmu_notifier() {
...
case BUS_NOTIFY_ADD_DEVICE:
if (dwc_pcie_register_dev(pdev))
return NOTIFY_BAD;
...
}
Does returning NOTIFY_BAD here halt the notifier chain?
If an allocation fails in dwc_pcie_register_dev(), returning NOTIFY_BAD
aborts the bus notifier chain. This can silently break other subsystems
like the IOMMU that rely on BUS_NOTIFY_ADD_DEVICE.
[ ... ]
> @@ -811,7 +820,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
> }
>
> /* Unwind when platform driver removes */
> - ret = devm_add_action_or_reset(&plat_dev->dev,
> + ret = devm_add_action_or_reset(&fdev->dev,
> dwc_pcie_pmu_remove_cpuhp_instance,
> &pcie_pmu->cpuhp_node);
> if (ret)
[Severity: High]
This is a pre-existing issue, but can this cause a NULL pointer dereference
if a CPU offline event happens during probe?
In dwc_pcie_pmu_probe(), cpuhp_state_add_instance() is called right before
this point, and perf_pmu_register() is called afterward. If a CPU offline
event occurs in that window:
drivers/perf/dwc_pcie_pmu.c:dwc_pcie_pmu_offline_cpu() {
...
perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target);
...
}
Because perf_pmu_register() hasn't been called yet, pmu->cpu_pmu_context is
not yet allocated. Does this cause perf_pmu_migrate_context() to dereference
a NULL cpu_pmu_context?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260629092717.74946-1-yang.yicong@picoheart.com?part=3
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
@ 2026-06-29 9:40 ` sashiko-bot
2026-06-29 17:43 ` Bjorn Helgaas
` (2 subsequent siblings)
3 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-29 9:40 UTC (permalink / raw)
To: Yicong Yang; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The new Picoheart entry in `dwc_pcie_rasdes_vsec_ids` breaks alphabetical sorting.
--
commit 86c4e43dc00cd979fb3b5c10fa079d5650a94f54
Author: Yicong Yang <yang.yicong@picoheart.com>
perf/dwc_pcie: Add support for Picoheart vendor devices
This commit adds PCI_VENDOR_ID_PICOHEART to pci_ids.h and updates the
DWC PCIe vendor table to enable PCIe PMU support for Picoheart devices.
> diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
> index 8ff778e7aec0e..b0ccb2ad74db7 100644
> --- a/include/linux/pcie-dwc.h
> +++ b/include/linux/pcie-dwc.h
[ ... ]
> @@ -32,6 +32,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> { .vendor_id = PCI_VENDOR_ID_SAMSUNG,
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> + { .vendor_id = PCI_VENDOR_ID_PICOHEART,
> + .vsec_id = 0x02, .vsec_rev = 0x4 },
[Severity: Low]
This isn't a bug, but should PCI_VENDOR_ID_PICOHEART be placed between
PCI_VENDOR_ID_AMPERE and PCI_VENDOR_ID_QCOM to maintain the alphabetical
sorting of the existing entries in dwc_pcie_rasdes_vsec_ids[]?
> {}
> };
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260629092717.74946-1-yang.yicong@picoheart.com?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring
2026-06-29 9:27 ` [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring Yicong Yang
@ 2026-06-29 9:42 ` sashiko-bot
2026-06-30 8:50 ` Shuai Xue
1 sibling, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-29 9:42 UTC (permalink / raw)
To: Yicong Yang; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing PM runtime reference during asynchronous hrtimer lifecycle
--
commit 2e4285bc1c7667cda860c906d0df29f1548de698
Author: Yufan Dou <douyufan@picoheart.com>
perf/dwc_pcie: Support narrowed time-based counter for long time monitoring
This commit adds an optional hrtimer that fires every 2 seconds to read,
update, and reset the time-based counter. This works around hardware
implementations that do not implement the full 64-bit width of the counter
and limits short time monitoring usage.
> diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
> index 5385401fa9cf6..7ec8302d4090b 100644
> --- a/drivers/perf/dwc_pcie_pmu.c
> +++ b/drivers/perf/dwc_pcie_pmu.c
[ ... ]
> @@ -429,6 +453,26 @@ static int dwc_pcie_pmu_validate_group(struct perf_event *event)
> return 0;
> }
>
> +static enum hrtimer_restart dwc_pcie_pmu_hrtimer_callback(struct hrtimer *hrtimer)
> +{
> + struct dwc_pcie_pmu *pcie_pmu = container_of(hrtimer, struct dwc_pcie_pmu, hrtimer);
> + struct perf_event *event = pcie_pmu->time_based_event;
> + struct hw_perf_event *hwc;
> +
> + if (!event)
> + return HRTIMER_NORESTART;
> +
> + hwc = &event->hw;
> + if (hwc->state & PERF_HES_STOPPED)
> + return HRTIMER_NORESTART;
> +
> + dwc_pcie_pmu_event_update(event);
> + dwc_pcie_pmu_reset_time_based_counter(event);
> + hrtimer_forward_now(hrtimer, ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS));
[Severity: High]
If the PCIe Root Port enters runtime suspend (such as D3hot or D3cold) while
this timer is active, will the hardware accesses in
dwc_pcie_pmu_event_update() cause problems?
Accessing the configuration space of a suspended device could return
corrupted data or trigger system errors like synchronous external aborts.
> +
> + return HRTIMER_RESTART;
> +}
[ ... ]
> @@ -478,10 +522,15 @@ static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags)
> hwc->state = 0;
> local64_set(&hwc->prev_count, 0);
>
> - if (type == DWC_PCIE_LANE_EVENT)
> + if (type == DWC_PCIE_LANE_EVENT) {
> dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, true);
> - else if (type == DWC_PCIE_TIME_BASE_EVENT)
> + } else if (type == DWC_PCIE_TIME_BASE_EVENT) {
> dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
> + if (pcie_pmu->timer_enable)
> + hrtimer_start(&pcie_pmu->hrtimer,
> + ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS),
> + HRTIMER_MODE_REL_PINNED_HARD);
[Severity: High]
Should we be taking a PM runtime reference on the PCIe Root Port device when
starting this background hrtimer?
Since the hrtimer fires asynchronously every 2 seconds, ensuring the device
remains awake while the event is active would prevent the hardware accesses
in the timer callback from hitting a powered-down device.
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260629092717.74946-1-yang.yicong@picoheart.com?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
2026-06-29 9:40 ` sashiko-bot
@ 2026-06-29 17:43 ` Bjorn Helgaas
2026-06-30 3:35 ` Shuai Xue
2026-06-30 11:31 ` Niklas Cassel
3 siblings, 0 replies; 12+ messages in thread
From: Bjorn Helgaas @ 2026-06-29 17:43 UTC (permalink / raw)
To: Yicong Yang
Cc: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel, jingoohan1, mani, juwenlong, geshijian,
douyufan
On Mon, Jun 29, 2026 at 05:27:15PM +0800, Yicong Yang wrote:
> Add PCI_VENDOR_ID_PICOHEART in pci_ids.h. Update the DWC PCIe
> vendor table with Picoheart PCIe vendorid to enable the PCIe
> PMU support.
s/vendorid/Vendor ID/
> Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
> ---
> include/linux/pci_ids.h | 2 ++
> include/linux/pcie-dwc.h | 2 ++
> 2 files changed, 4 insertions(+)
>
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 1c9d40e09107..2c17239aacea 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2640,6 +2640,8 @@
> #define PCI_VENDOR_ID_SUNIX 0x1fd4
> #define PCI_DEVICE_ID_SUNIX_1999 0x1999
>
> +#define PCI_VENDOR_ID_PICOHEART 0x20fa
> +
> #define PCI_VENDOR_ID_HINT 0x3388
> #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
>
> diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
> index 8ff778e7aec0..b0ccb2ad74db 100644
> --- a/include/linux/pcie-dwc.h
> +++ b/include/linux/pcie-dwc.h
> @@ -32,6 +32,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> { .vendor_id = PCI_VENDOR_ID_SAMSUNG,
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> + { .vendor_id = PCI_VENDOR_ID_PICOHEART,
> + .vsec_id = 0x02, .vsec_rev = 0x4 },
> {}
> };
>
> --
> 2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
2026-06-29 9:40 ` sashiko-bot
2026-06-29 17:43 ` Bjorn Helgaas
@ 2026-06-30 3:35 ` Shuai Xue
2026-06-30 11:31 ` Niklas Cassel
3 siblings, 0 replies; 12+ messages in thread
From: Shuai Xue @ 2026-06-30 3:35 UTC (permalink / raw)
To: Yicong Yang, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, douyufan
On 6/29/26 5:27 PM, Yicong Yang wrote:
> Add PCI_VENDOR_ID_PICOHEART in pci_ids.h. Update the DWC PCIe
> vendor table with Picoheart PCIe vendorid to enable the PCIe
> PMU support.
>
> Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
> ---
> include/linux/pci_ids.h | 2 ++
> include/linux/pcie-dwc.h | 2 ++
> 2 files changed, 4 insertions(+)
>
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 1c9d40e09107..2c17239aacea 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2640,6 +2640,8 @@
> #define PCI_VENDOR_ID_SUNIX 0x1fd4
> #define PCI_DEVICE_ID_SUNIX_1999 0x1999
>
> +#define PCI_VENDOR_ID_PICOHEART 0x20fa
> +
> #define PCI_VENDOR_ID_HINT 0x3388
> #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
>
> diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
> index 8ff778e7aec0..b0ccb2ad74db 100644
> --- a/include/linux/pcie-dwc.h
> +++ b/include/linux/pcie-dwc.h
> @@ -32,6 +32,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> { .vendor_id = PCI_VENDOR_ID_SAMSUNG,
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> + { .vendor_id = PCI_VENDOR_ID_PICOHEART,
> + .vsec_id = 0x02, .vsec_rev = 0x4 },
> {}
> };
>
Hi Yicong,
The pci_ids.h part looks good to me. Minor, the new entry in
pcie-dwc.h breaks the alphabetical ordering of the table
Could you please move the PICOHEART entry between AMPERE and QCOM to
keep the list sorted alphabetically?
With that fixed,
Reviewed-by: Shuai Xue <xueshuai@linux.alibaba.com>
Thanks.
Shuai
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring
2026-06-29 9:27 ` [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring Yicong Yang
2026-06-29 9:42 ` sashiko-bot
@ 2026-06-30 8:50 ` Shuai Xue
1 sibling, 0 replies; 12+ messages in thread
From: Shuai Xue @ 2026-06-30 8:50 UTC (permalink / raw)
To: Yicong Yang, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, douyufan
On 6/29/26 5:27 PM, Yicong Yang wrote:
> From: Yufan Dou <douyufan@picoheart.com>
>
> The DWC PCIe Time-Based Analysis Data Register (the counter for time-based
> events) is architected as 64-bit, but some hardware implementations do not
> implement the full width. On these implementations the counter stops after
> reaching its implemented width. This will limit the usage for short time
> monitoring only. The counter will only cover ~15s for monitoring RX TLP
> payloads on our platform.
>
> Add an optional hrtimer that fires every 2 seconds. It'll take the role
> as the counter overflow interrupt to read-update-reset the counter and
> event counts to break the limits of the narrow counters. It'll only
> apply on timer-based counter. The 2 seconds update period is the half
> of the maximum counting period (4s) of the time-based counter under
> period counting mode of the hardware.
>
> Because fully-implemented 64-bit counters do not need this workaround,
> enable this hrtimer on the platforms known to have narrowed counter.
>
> Before this patch, when counting fio for 10m the counts is incorrect:
> root@localhost:/tmp# perf stat -e dwc_rootport_20000/rx_pcie_tlp_data_payload/ -- fio --runtime=10m fio_job.config
> [...]
> Run status group 0 (all jobs):
> READ: bw=5594MiB/s (5865MB/s), 5594MiB/s-5594MiB/s (5865MB/s-5865MB/s), io=3278GiB (3519GB), run=600010-600010msec
> [...]
> Performance counter stats for 'system wide':
> 137,438,953,456 dwc_rootport_20000/rx_pcie_tlp_data_payload/
>
> After this patch the counts is as expected:
> root@localhost:/tmp# perf stat -e dwc_rootport_20000/rx_pcie_tlp_data_payload/ -- fio --runtime=10m fio_job.config
> [...]
> Run status group 0 (all jobs):
> READ: bw=5632MiB/s (5905MB/s), 5632MiB/s-5632MiB/s (5905MB/s-5905MB/s), io=3300GiB (3543GB), run=600013-600013msec
> [...]
> Performance counter stats for 'system wide':
> 3,543,850,268,576 dwc_rootport_20000/rx_pcie_tlp_data_payload/
>
> Signed-off-by: Yufan Dou <douyufan@picoheart.com>
> Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
> ---
> drivers/perf/dwc_pcie_pmu.c | 69 ++++++++++++++++++++++++++++++++++---
> 1 file changed, 65 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
> index 5385401fa9cf..7ec8302d4090 100644
> --- a/drivers/perf/dwc_pcie_pmu.c
> +++ b/drivers/perf/dwc_pcie_pmu.c
> @@ -11,6 +11,7 @@
> #include <linux/cpumask.h>
> #include <linux/device.h>
> #include <linux/errno.h>
> +#include <linux/hrtimer.h>
> #include <linux/kernel.h>
> #include <linux/list.h>
> #include <linux/pcie-dwc.h>
> @@ -83,6 +84,7 @@ enum dwc_pcie_event_type {
>
> #define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0)
> #define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0)
> +#define DWC_PCIE_PMU_TIMER_PERIOD_NS (2 * NSEC_PER_SEC)
>
> struct dwc_pcie_pmu {
> struct pmu pmu;
> @@ -93,6 +95,8 @@ struct dwc_pcie_pmu {
> /* Groups #6 and #7 */
> DECLARE_BITMAP(lane_events, 2 * DWC_PCIE_LANE_MAX_EVENTS_PER_GROUP);
> struct perf_event *time_based_event;
> + bool timer_enable;
> + struct hrtimer hrtimer;
>
> struct hlist_node cpuhp_node;
> int on_cpu;
> @@ -354,6 +358,26 @@ static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event)
> return val;
> }
>
> +static void dwc_pcie_pmu_reset_time_based_counter(struct perf_event *event)
> +{
> + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
> + struct hw_perf_event *hwc = &event->hw;
> + u64 prev;
> +
> + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
> +
> + /*
> + * The hardware counter is reset to zero when disabled. Synchronize
> + * prev_count so that the next event_update() computes the correct
> + * delta against the new counter baseline.
> + */
> + do {
> + prev = local64_read(&hwc->prev_count);
> + } while (local64_cmpxchg(&hwc->prev_count, prev, 0) != prev);
> +
> + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
> +}
> +
> static void dwc_pcie_pmu_event_update(struct perf_event *event)
> {
> struct hw_perf_event *hwc = &event->hw;
> @@ -429,6 +453,26 @@ static int dwc_pcie_pmu_validate_group(struct perf_event *event)
> return 0;
> }
>
> +static enum hrtimer_restart dwc_pcie_pmu_hrtimer_callback(struct hrtimer *hrtimer)
> +{
> + struct dwc_pcie_pmu *pcie_pmu = container_of(hrtimer, struct dwc_pcie_pmu, hrtimer);
> + struct perf_event *event = pcie_pmu->time_based_event;
> + struct hw_perf_event *hwc;
> +
> + if (!event)
> + return HRTIMER_NORESTART;
> +
> + hwc = &event->hw;
> + if (hwc->state & PERF_HES_STOPPED)
> + return HRTIMER_NORESTART;
> +
> + dwc_pcie_pmu_event_update(event);
> + dwc_pcie_pmu_reset_time_based_counter(event);
> + hrtimer_forward_now(hrtimer, ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS));
From sashiko:
If the PCIe Root Port enters runtime suspend (such as D3hot or D3cold) while
this timer is active, will the hardware accesses in
dwc_pcie_pmu_event_update() cause problems?
Accessing the configuration space of a suspended device could return
corrupted data or trigger system errors like synchronous external aborts.
https://sashiko.dev/#/patchset/20260629092717.74946-1-yang.yicong%40picoheart.com
> +
> + return HRTIMER_RESTART;
> +}
> +
> static int dwc_pcie_pmu_event_init(struct perf_event *event)
> {
> struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
> @@ -478,10 +522,15 @@ static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags)
> hwc->state = 0;
> local64_set(&hwc->prev_count, 0);
>
> - if (type == DWC_PCIE_LANE_EVENT)
> + if (type == DWC_PCIE_LANE_EVENT) {
> dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, true);
> - else if (type == DWC_PCIE_TIME_BASE_EVENT)
> + } else if (type == DWC_PCIE_TIME_BASE_EVENT) {
> dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
> + if (pcie_pmu->timer_enable)
> + hrtimer_start(&pcie_pmu->hrtimer,
> + ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS),
> + HRTIMER_MODE_REL_PINNED_HARD);
> + }
> }
>
> static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
> @@ -495,10 +544,12 @@ static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
>
> dwc_pcie_pmu_event_update(event);
>
> - if (type == DWC_PCIE_LANE_EVENT)
> + if (type == DWC_PCIE_LANE_EVENT) {
> dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, false);
> - else if (type == DWC_PCIE_TIME_BASE_EVENT)
> + } else if (type == DWC_PCIE_TIME_BASE_EVENT) {
> dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
> + hrtimer_cancel(&pcie_pmu->hrtimer);
While hrtimer_cancel() on an inactive timer is a no-op, it is asymmetric
with event_start which only starts the timer when timer_enable is true.
Please guard it for consistency:
if (pcie_pmu->timer_enable)
hrtimer_cancel(&pcie_pmu->hrtimer);
Thanks.
Shuai
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface
2026-06-29 9:27 ` [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface Yicong Yang
2026-06-29 9:40 ` sashiko-bot
@ 2026-06-30 9:19 ` Shuai Xue
1 sibling, 0 replies; 12+ messages in thread
From: Shuai Xue @ 2026-06-30 9:19 UTC (permalink / raw)
To: Yicong Yang, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel
Cc: jingoohan1, mani, juwenlong, geshijian, douyufan
On 6/29/26 5:27 PM, Yicong Yang wrote:
> The DWC PCIe PMU makes use of the platform device interface but
> is not the real device, it's actually the RAS DES capability of
> the root port. It's more appropriate to use the lightweight
> faux framework to abstract this, it'll be more simple and no
> need for the complete device and drivers model. So convert to
> the faux device interface.
>
> Move the cpuhp state registration prior to faux device creation
> since the probe depends on this and will run by the faux device
> creation.
>
> No functional changes intended.
>
> Reviewed-by: Jonathan Cameron <jic23@kernel.org>
> Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
> ---
> drivers/perf/dwc_pcie_pmu.c | 101 +++++++++++++++++-------------------
> 1 file changed, 48 insertions(+), 53 deletions(-)
>
> diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c
> index 7ec8302d4090..3a9965b5abf4 100644
> --- a/drivers/perf/dwc_pcie_pmu.c
> +++ b/drivers/perf/dwc_pcie_pmu.c
> @@ -10,6 +10,7 @@
> #include <linux/cpuhotplug.h>
> #include <linux/cpumask.h>
> #include <linux/device.h>
> +#include <linux/device/faux.h>
> #include <linux/errno.h>
> #include <linux/hrtimer.h>
> #include <linux/kernel.h>
> @@ -17,7 +18,6 @@
> #include <linux/pcie-dwc.h>
> #include <linux/perf_event.h>
> #include <linux/pci.h>
> -#include <linux/platform_device.h>
> #include <linux/smp.h>
> #include <linux/sysfs.h>
> #include <linux/types.h>
> @@ -110,9 +110,10 @@ static struct list_head dwc_pcie_dev_info_head =
> static bool notify;
>
> struct dwc_pcie_dev_info {
> - struct platform_device *plat_dev;
> + struct faux_device *fdev;
> struct pci_dev *pdev;
> struct list_head dev_node;
> + char *name;
> };
>
> static ssize_t cpumask_show(struct device *dev,
> @@ -658,6 +659,12 @@ static void dwc_pcie_unregister_pmu(void *data)
> perf_pmu_unregister(&pcie_pmu->pmu);
> }
>
> +static int dwc_pcie_pmu_probe(struct faux_device *fdev);
> +
> +static struct faux_device_ops dwc_pcie_faux_ops = {
> + .probe = dwc_pcie_pmu_probe,
> +};
> +
> static u16 dwc_pcie_des_cap(struct pci_dev *pdev)
> {
> const struct dwc_pcie_vsec_id *vid;
> @@ -684,31 +691,41 @@ static u16 dwc_pcie_des_cap(struct pci_dev *pdev)
>
> static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info)
> {
> - platform_device_unregister(dev_info->plat_dev);
> + faux_device_destroy(dev_info->fdev);
> list_del(&dev_info->dev_node);
> + kfree(dev_info->name);
> kfree(dev_info);
> }
>
> static int dwc_pcie_register_dev(struct pci_dev *pdev)
> {
> - struct platform_device *plat_dev;
> struct dwc_pcie_dev_info *dev_info;
> + struct faux_device *fdev;
> + char *name;
> u32 sbdf;
>
> sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
> - plat_dev = platform_device_register_simple("dwc_pcie_pmu", sbdf, NULL, 0);
> - if (IS_ERR(plat_dev))
> - return PTR_ERR(plat_dev);
> + name = kasprintf(GFP_KERNEL, "dwc_pcie_pmu_%x", sbdf);
> + if (!name)
> + return -ENOMEM;
> +
> + fdev = faux_device_create(name, &pdev->dev, &dwc_pcie_faux_ops);
> + if (!fdev) {
> + kfree(name);
> + return -ENODEV;
> + }
>
> dev_info = kzalloc_obj(*dev_info);
> if (!dev_info) {
> - platform_device_unregister(plat_dev);
> + faux_device_destroy(fdev);
> + kfree(name);
> return -ENOMEM;
> }
>
> - /* Cache platform device to handle pci device hotplug */
> - dev_info->plat_dev = plat_dev;
> + /* Cache faux device to handle pci device hotplug */
> + dev_info->fdev = fdev;
> dev_info->pdev = pdev;
> + dev_info->name = name;
> list_add(&dev_info->dev_node, &dwc_pcie_dev_info_head);
>
> return 0;
> @@ -743,33 +760,25 @@ static struct notifier_block dwc_pcie_pmu_nb = {
> .notifier_call = dwc_pcie_pmu_notifier,
> };
>
> -static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
> +static int dwc_pcie_pmu_probe(struct faux_device *fdev)
> {
> - struct pci_dev *pdev;
> + struct pci_dev *pdev = to_pci_dev(fdev->dev.parent);
> 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;
> - }
> + sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
> + name = devm_kasprintf(&fdev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf);
> + if (!name)
> + return -ENOMEM;
>
> vsec = dwc_pcie_des_cap(pdev);
> if (!vsec)
> return -ENODEV;
>
> - pci_dev_put(pdev);
> - name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf);
> - if (!name)
> - return -ENOMEM;
> -
> - pcie_pmu = devm_kzalloc(&plat_dev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
> + pcie_pmu = devm_kzalloc(&fdev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
> if (!pcie_pmu)
> return -ENOMEM;
>
> @@ -789,7 +798,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
>
> pcie_pmu->pmu = (struct pmu){
> .name = name,
> - .parent = &plat_dev->dev,
> + .parent = &fdev->dev,
> .module = THIS_MODULE,
> .attr_groups = dwc_pcie_attr_groups,
> .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
> @@ -811,7 +820,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
> }
>
> /* Unwind when platform driver removes */
Minor: a stale comment.
Reviewed-by: Shuai Xue <xueshuai@linux.alibaba.com>
Thanks.
Shuai
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
` (2 preceding siblings ...)
2026-06-30 3:35 ` Shuai Xue
@ 2026-06-30 11:31 ` Niklas Cassel
3 siblings, 0 replies; 12+ messages in thread
From: Niklas Cassel @ 2026-06-30 11:31 UTC (permalink / raw)
To: Yicong Yang
Cc: xueshuai, renyu.zj, will, mark.rutland, jic23, bhelgaas,
linux-pci, linux-kernel, jingoohan1, mani, juwenlong, geshijian,
douyufan
On Mon, Jun 29, 2026 at 05:27:15PM +0800, Yicong Yang wrote:
> Add PCI_VENDOR_ID_PICOHEART in pci_ids.h. Update the DWC PCIe
> vendor table with Picoheart PCIe vendorid to enable the PCIe
> PMU support.
>
> Signed-off-by: Yicong Yang <yang.yicong@picoheart.com>
> ---
> include/linux/pci_ids.h | 2 ++
> include/linux/pcie-dwc.h | 2 ++
> 2 files changed, 4 insertions(+)
>
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 1c9d40e09107..2c17239aacea 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2640,6 +2640,8 @@
> #define PCI_VENDOR_ID_SUNIX 0x1fd4
> #define PCI_DEVICE_ID_SUNIX_1999 0x1999
>
> +#define PCI_VENDOR_ID_PICOHEART 0x20fa
> +
> #define PCI_VENDOR_ID_HINT 0x3388
> #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
>
> diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
> index 8ff778e7aec0..b0ccb2ad74db 100644
> --- a/include/linux/pcie-dwc.h
> +++ b/include/linux/pcie-dwc.h
> @@ -32,6 +32,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> { .vendor_id = PCI_VENDOR_ID_SAMSUNG,
> .vsec_id = 0x02, .vsec_rev = 0x4 },
> + { .vendor_id = PCI_VENDOR_ID_PICOHEART,
> + .vsec_id = 0x02, .vsec_rev = 0x4 },
Could you please point to the in-tree device driver for this
DWC based "picoheart" PCIe controller?
$ git grep -i picoheart drivers/pci/controller/dwc/
<nothing>
It feels wrong to add PMU support for a PCIe controller,
if that PCIe controller does not have support in mainline.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-30 11:31 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-29 9:27 [PATCH v2 0/3] New vendor support and optimizations for DWC PCIe PMU Yicong Yang
2026-06-29 9:27 ` [PATCH v2 1/3] perf/dwc_pcie: Add support for Picoheart vendor devices Yicong Yang
2026-06-29 9:40 ` sashiko-bot
2026-06-29 17:43 ` Bjorn Helgaas
2026-06-30 3:35 ` Shuai Xue
2026-06-30 11:31 ` Niklas Cassel
2026-06-29 9:27 ` [PATCH v2 2/3] perf/dwc_pcie: Support narrowed time-based counter for long time monitoring Yicong Yang
2026-06-29 9:42 ` sashiko-bot
2026-06-30 8:50 ` Shuai Xue
2026-06-29 9:27 ` [PATCH v2 3/3] perf/dwc_pcie: Convert to faux device interface Yicong Yang
2026-06-29 9:40 ` sashiko-bot
2026-06-30 9:19 ` Shuai Xue
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox