drivers/perf: xgene: Add CPU hotplug support
If the CPU assigned to the xgene PMU is taken offline, then subsequent perf invocations on the PMU will fail: # echo 0 > /sys/devices/system/cpu/cpu0/online # perf stat -a -e l3c0/cycle-count/,l3c0/write/ sleep 1 Error: The sys_perf_event_open() syscall returned with 19 (No such device) for event (l3c0/cycle-count/). /bin/dmesg may provide additional information. No CONFIG_PERF_EVENTS=y kernel support configured? This patch implements a hotplug notifier in the xgene PMU driver so that the PMU context is migrated to another online CPU should its assigned CPU disappear. Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Hoan Tran <hoan.tran@amperecomputing.com> [will: Made naming of new cpuhp_state enum entry consistent] Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
parent
472dc9fa7c
commit
cbb72a3c19
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -130,12 +131,14 @@ struct xgene_pmu_ops {
|
|||
|
||||
struct xgene_pmu {
|
||||
struct device *dev;
|
||||
struct hlist_node node;
|
||||
int version;
|
||||
void __iomem *pcppmu_csr;
|
||||
u32 mcb_active_mask;
|
||||
u32 mc_active_mask;
|
||||
u32 l3c_active_mask;
|
||||
cpumask_t cpu;
|
||||
int irq;
|
||||
raw_spinlock_t lock;
|
||||
const struct xgene_pmu_ops *ops;
|
||||
struct list_head l3cpmus;
|
||||
|
@ -1806,6 +1809,53 @@ static const struct acpi_device_id xgene_pmu_acpi_match[] = {
|
|||
MODULE_DEVICE_TABLE(acpi, xgene_pmu_acpi_match);
|
||||
#endif
|
||||
|
||||
static int xgene_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
|
||||
{
|
||||
struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu,
|
||||
node);
|
||||
|
||||
if (cpumask_empty(&xgene_pmu->cpu))
|
||||
cpumask_set_cpu(cpu, &xgene_pmu->cpu);
|
||||
|
||||
/* Overflow interrupt also should use the same CPU */
|
||||
WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
|
||||
{
|
||||
struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu,
|
||||
node);
|
||||
struct xgene_pmu_dev_ctx *ctx;
|
||||
unsigned int target;
|
||||
|
||||
if (!cpumask_test_and_clear_cpu(cpu, &xgene_pmu->cpu))
|
||||
return 0;
|
||||
target = cpumask_any_but(cpu_online_mask, cpu);
|
||||
if (target >= nr_cpu_ids)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(ctx, &xgene_pmu->mcpmus, next) {
|
||||
perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target);
|
||||
}
|
||||
list_for_each_entry(ctx, &xgene_pmu->mcbpmus, next) {
|
||||
perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target);
|
||||
}
|
||||
list_for_each_entry(ctx, &xgene_pmu->l3cpmus, next) {
|
||||
perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target);
|
||||
}
|
||||
list_for_each_entry(ctx, &xgene_pmu->iobpmus, next) {
|
||||
perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target);
|
||||
}
|
||||
|
||||
cpumask_set_cpu(target, &xgene_pmu->cpu);
|
||||
/* Overflow interrupt also should use the same CPU */
|
||||
WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct xgene_pmu_data *dev_data;
|
||||
|
@ -1815,6 +1865,14 @@ static int xgene_pmu_probe(struct platform_device *pdev)
|
|||
int irq, rc;
|
||||
int version;
|
||||
|
||||
/* Install a hook to update the reader CPU in case it goes offline */
|
||||
rc = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE,
|
||||
"CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE",
|
||||
xgene_pmu_online_cpu,
|
||||
xgene_pmu_offline_cpu);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
xgene_pmu = devm_kzalloc(&pdev->dev, sizeof(*xgene_pmu), GFP_KERNEL);
|
||||
if (!xgene_pmu)
|
||||
return -ENOMEM;
|
||||
|
@ -1865,6 +1923,7 @@ static int xgene_pmu_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr,
|
||||
IRQF_NOBALANCING | IRQF_NO_THREAD,
|
||||
dev_name(&pdev->dev), xgene_pmu);
|
||||
|
@ -1873,6 +1932,8 @@ static int xgene_pmu_probe(struct platform_device *pdev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
xgene_pmu->irq = irq;
|
||||
|
||||
raw_spin_lock_init(&xgene_pmu->lock);
|
||||
|
||||
/* Check for active MCBs and MCUs */
|
||||
|
@ -1883,13 +1944,11 @@ static int xgene_pmu_probe(struct platform_device *pdev)
|
|||
xgene_pmu->mc_active_mask = 0x1;
|
||||
}
|
||||
|
||||
/* Pick one core to use for cpumask attributes */
|
||||
cpumask_set_cpu(smp_processor_id(), &xgene_pmu->cpu);
|
||||
|
||||
/* Make sure that the overflow interrupt is handled by this CPU */
|
||||
rc = irq_set_affinity(irq, &xgene_pmu->cpu);
|
||||
/* Add this instance to the list used by the hotplug callback */
|
||||
rc = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE,
|
||||
&xgene_pmu->node);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Failed to set interrupt affinity!\n");
|
||||
dev_err(&pdev->dev, "Error %d registering hotplug", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1897,13 +1956,18 @@ static int xgene_pmu_probe(struct platform_device *pdev)
|
|||
rc = xgene_pmu_probe_pmu_dev(xgene_pmu, pdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "No PMU perf devices found!\n");
|
||||
return rc;
|
||||
goto out_unregister;
|
||||
}
|
||||
|
||||
/* Enable interrupt */
|
||||
xgene_pmu->ops->unmask_int(xgene_pmu);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE,
|
||||
&xgene_pmu->node);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1924,6 +1988,8 @@ static int xgene_pmu_remove(struct platform_device *pdev)
|
|||
xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->iobpmus);
|
||||
xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcbpmus);
|
||||
xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcpmus);
|
||||
cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE,
|
||||
&xgene_pmu->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -164,6 +164,7 @@ enum cpuhp_state {
|
|||
CPUHP_AP_PERF_ARM_L2X0_ONLINE,
|
||||
CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
|
||||
CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE,
|
||||
CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE,
|
||||
CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE,
|
||||
CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE,
|
||||
CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE,
|
||||
|
|
Loading…
Reference in New Issue