From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:25
v5:
Remove list of CPU types for ACPI systems. We now match a generic
event list, and use the PMCIED[01] to select events which exist on
the given PMU. This avoids the need to update the kernel every-time
a new CPU is released.
Update the maintainers list to include the new file.
v4:
Correct build issues with ARM (!ARM64) kernels.
Add ThunderX to list of PMU types.
v3:
Enable ARM performance monitoring units on ACPI/arm64 machines.
This patch expands and reworks the patches published by Mark Salter
in order to clean up a few of the previous review comments, as well as
add support for newer CPUs and big/little configurations.
Jeremy Linton (6):
arm64: pmu: Probe default hw/cache counters
arm: arm64: Add routine to determine cpuid of other cpus
arm: arm64: pmu: Assign platform PMU CPU affinity
arm64: pmu: Add routines for detecting differing PMU types in the
arm64: pmu: Enable multiple PMUs in an ACPI system
MAINTAINERS: Tweak ARM PMU maintainers
Mark Salter (2):
arm64: pmu: add fallback probe table
arm64: pmu: Add support for probing with ACPI
MAINTAINERS | 3 +-
arch/arm/include/asm/cputype.h | 6 +-
arch/arm64/include/asm/cputype.h | 4 +
arch/arm64/kernel/perf_event.c | 51 ++++++++-
arch/arm64/kernel/smp.c | 5 +
drivers/perf/Kconfig | 4 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu.c | 57 +++++++++--
drivers/perf/arm_pmu_acpi.c | 216 +++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 10 ++
10 files changed, 341 insertions(+), 16 deletions(-)
create mode 100644 drivers/perf/arm_pmu_acpi.c
--
2.5.5
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:26
From: Mark Salter <redacted>
In preparation for ACPI support, add a pmu_probe_info table to
the arm_pmu_device_probe() call. This table gets used when
probing in the absence of a devicetree node for PMU.
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 8 +++++++-
include/linux/perf/arm_pmu.h | 3 +++
2 files changed, 10 insertions(+), 1 deletion(-)
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:27
ARMv8 machines can identify the micro/arch defined counters
that are available on a machine. Add all these counters to the
default armv8 perf map. At run-time disable the counters which
are not available on the given PMU.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 45 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)
@@ -905,9 +924,22 @@ static void armv8pmu_reset(void *info)staticintarmv8_pmuv3_map_event(structperf_event*event){-returnarmpmu_map_event(event,&armv8_pmuv3_perf_map,-&armv8_pmuv3_perf_cache_map,-ARMV8_PMU_EVTYPE_EVENT);+inthw_event_id;+structarm_pmu*armpmu=to_arm_pmu(event->pmu);++hw_event_id=armpmu_map_event(event,&armv8_pmuv3_perf_map,+&armv8_pmuv3_perf_cache_map,+ARMV8_PMU_EVTYPE_EVENT);+if(hw_event_id<0)+returnhw_event_id;++/* disable micro/arch events not supported by this PMU */+if((hw_event_id<ARMV8_PMUV3_MAX_COMMON_EVENTS)&&+!test_bit(hw_event_id,armpmu->pmceid_bitmap)){+return-EOPNOTSUPP;+}++returnhw_event_id;}staticintarmv8_a53_map_event(structperf_event*event)
@@ -1044,8 +1076,13 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {{},};+/*+*NonDTsystemshavetheirmicro/archeventsprobedatrun-time.+*Afairlycompletelistofgenericeventsareprovidedandonesthat+*aren'tsupportedbythecurrentPMUaredisabled.+*/staticconststructpmu_probe_infoarmv8_pmu_probe_table[]={-PMU_PROBE(0,0,armv8_pmuv3_init),/* if all else fails... */+PMU_PROBE(0,0,armv8_pmuv3_init),/* enable all defined counters */{/* sentinel value */}};
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:28
From: Mark Salter <redacted>
In the case of ACPI, the PMU IRQ information is contained in the
MADT table. Also, since the PMU does not exist as a device in the
ACPI DSDT table, it is necessary to create a platform device so
that the appropriate driver probing is triggered.
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Jeremy Linton <redacted>
---
NOTE: Much of the code in pmu_acpi_init() is replaced in a later version
of this patch. The later version of the patch cleans up some of the
possible style/error handling issues that have been pointed out with
this version.
arch/arm64/kernel/smp.c | 5 +++
drivers/perf/Kconfig | 4 ++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 ++++
5 files changed, 114 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
@@ -0,0 +1,97 @@+/*+*PMUsupport+*+*Copyright(C)2015RedHatInc.+*Author:MarkSalter<msalter@redhat.com>+*+*ThisworkislicensedunderthetermsoftheGNUGPL,version2.See+*theCOPYINGfileinthetop-leveldirectory.+*+*/++#include<linux/perf/arm_pmu.h>+#include<linux/platform_device.h>+#include<linux/acpi.h>+#include<linux/irq.h>+#include<linux/irqdesc.h>++#define PMU_PDEV_NAME "armv8-pmu"++structpmu_irq{+intgsi;+inttrigger;+};++staticstructpmu_irqpmu_irqs[NR_CPUS]__initdata;++void__initarm_pmu_parse_acpi(intcpu,structacpi_madt_generic_interrupt*gic)+{+pmu_irqs[cpu].gsi=gic->performance_interrupt;+if(gic->flags&ACPI_MADT_PERFORMANCE_IRQ_MODE)+pmu_irqs[cpu].trigger=ACPI_EDGE_SENSITIVE;+else+pmu_irqs[cpu].trigger=ACPI_LEVEL_SENSITIVE;+}++staticint__initpmu_acpi_init(void)+{+structplatform_device*pdev;+structpmu_irq*pirq=pmu_irqs;+structresource*res,*r;+interr=-ENOMEM;+inti,count,irq;++if(acpi_disabled)+return0;++/* Must have irq for boot boot cpu, at least */+if(pirq->gsi==0)+return-EINVAL;++irq=acpi_register_gsi(NULL,pirq->gsi,pirq->trigger,+ACPI_ACTIVE_HIGH);++if(irq_is_percpu(irq))+count=1;+else+for(i=1,count=1;i<NR_CPUS;i++)+if(pmu_irqs[i].gsi)+++count;++pdev=platform_device_alloc(PMU_PDEV_NAME,-1);+if(!pdev)+gotoerr_free_gsi;++res=kcalloc(count,sizeof(*res),GFP_KERNEL);+if(!res)+gotoerr_free_device;++for(i=0,r=res;i<count;i++,pirq++,r++){+if(i)+irq=acpi_register_gsi(NULL,pirq->gsi,pirq->trigger,+ACPI_ACTIVE_HIGH);+r->start=r->end=irq;+r->flags=IORESOURCE_IRQ;+if(pirq->trigger==ACPI_EDGE_SENSITIVE)+r->flags|=IORESOURCE_IRQ_HIGHEDGE;+else+r->flags|=IORESOURCE_IRQ_HIGHLEVEL;+}++err=platform_device_add_resources(pdev,res,count);+if(!err)+err=platform_device_add(pdev);+kfree(res);+if(!err)+return0;++err_free_device:+platform_device_put(pdev);++err_free_gsi:+for(i=0;i<count;i++)+acpi_unregister_gsi(pmu_irqs[i].gsi);++returnerr;+}+arch_initcall(pmu_acpi_init);
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:29
It is helpful if we can read the cpuid/midr of other CPUs
in the system independent of arm/arm64.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm/include/asm/cputype.h | 6 +++++-
arch/arm64/include/asm/cputype.h | 4 ++++
2 files changed, 9 insertions(+), 1 deletion(-)
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:30
On systems with multiple PMU types the PMU to CPU affinity
needs to be detected and set. The CPU to interrupt affinity
should also be set.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 52 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 42 insertions(+), 10 deletions(-)
@@ -872,25 +874,56 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)}/*-*CPUPMUidentificationandprobing.+*CPUPMUidentificationandprobing.Itspossibletohave+*multipleCPUtypesinanARMmachine.Assurethatweare+*pickingtherightPMUtypesbasedontheCPUinquestion*/-staticintprobe_current_pmu(structarm_pmu*pmu,-conststructpmu_probe_info*info)+staticintprobe_plat_pmu(structarm_pmu*pmu,+conststructpmu_probe_info*info,+unsignedintpmuid){-intcpu=get_cpu();-unsignedintcpuid=read_cpuid_id();intret=-ENODEV;+intcpu;+intaff_ctr=0;+structplatform_device*pdev=pmu->plat_device;+intirq=platform_get_irq(pdev,0);-pr_info("probing PMU on CPU %d\n",cpu);+if(irq>=0&&!irq_is_percpu(irq)){+pmu->irq_affinity=kcalloc(pdev->num_resources,sizeof(int),+GFP_KERNEL);+if(!pmu->irq_affinity)+return-ENOMEM;+}++for_each_possible_cpu(cpu){+unsignedintcpuid=read_specific_cpuid(cpu);++if(cpuid==pmuid){+cpumask_set_cpu(cpu,&pmu->supported_cpus);+pr_devel("enable pmu on cpu %d\n",cpu);+if(pmu->irq_affinity){+pmu->irq_affinity[aff_ctr]=cpu;+aff_ctr++;+}+}+}+pr_debug("probing PMU %X\n",pmuid);+/* find the type of PMU given the CPU */for(;info->init!=NULL;info++){-if((cpuid&info->mask)!=info->cpuid)+if((pmuid&info->mask)!=info->cpuid)continue;+pr_devel("Found PMU\n");ret=info->init(pmu);+if(!info->cpuid){+pmu->name=kasprintf(GFP_KERNEL,"%s_0x%x",+pmu->name,ARM_PARTNUM(pmuid));+if(!pmu->name)+return-ENOMEM;+}break;}-put_cpu();returnret;}
@@ -1016,8 +1049,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}else{-cpumask_setall(&pmu->supported_cpus);-ret=probe_current_pmu(pmu,probe_table);+ret=probe_plat_pmu(pmu,probe_table,read_cpuid_id());}if(ret){
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:31
In preparation for enabling heterogeneous PMUs on ACPI systems
add routines that detect this and group the resulting PMUs and
interrupts.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu_acpi.c | 135 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 132 insertions(+), 3 deletions(-)
@@ -31,6 +42,124 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)pmu_irqs[cpu].trigger=ACPI_EDGE_SENSITIVE;elsepmu_irqs[cpu].trigger=ACPI_LEVEL_SENSITIVE;+pr_devel("Assign CPU %d girq %d level %d\n",cpu,pmu_irqs[cpu].gsi,+pmu_irqs[cpu].trigger);+}++/* Count number and type of CPU cores in the system. */+void__initarm_pmu_acpi_determine_cpu_types(structpmu_types*pmus)+{+inti,j;++for_each_possible_cpu(i){+structcpuinfo_arm64*cinfo=per_cpu_ptr(&cpu_data,i);+u32partnum=MIDR_PARTNUM(cinfo->reg_midr);++pr_devel("Present CPU %d is a %X\n",i,partnum);+for(j=0;j<num_possible_cpus();j++){+if(pmus[j].cpu_type==partnum){+pmus[j].cpu_count++;+break;+}+if(pmus[j].cpu_count==0){+pmus[j].cpu_type=partnum;+pmus[j].cpu_count++;+break;+}+}+}+}++/*+*RegistersthegroupofPMUinterfaceswhichcorrispondtothe'last_cpu_id'.+*Thisgrouputlizes'count'resourcesinthe'res'.+*/+int__initarm_pmu_acpi_register_pmu(intcount,structresource*res,+intlast_cpu_id)+{+inti;+interr=-ENOMEM;+boolfree_gsi=false;+structplatform_device*pdev;++if(count){+pdev=platform_device_alloc(PMU_PDEV_NAME,last_cpu_id);+if(pdev){+err=platform_device_add_resources(pdev,+res,count);+if(!err){+err=platform_device_add(pdev);+if(err){+pr_warn("Unable to register PMU device\n");+free_gsi=true;+}+}else{+pr_warn("Unable to add resources to device\n");+free_gsi=true;+platform_device_put(pdev);+}+}else{+pr_warn("Unable to allocate platform device\n");+free_gsi=true;+}+}++/* unmark (and possibly unregister) registered GSIs */+for_each_possible_cpu(i){+if(pmu_irqs[i].registered){+if(free_gsi)+acpi_unregister_gsi(pmu_irqs[i].gsi);+pmu_irqs[i].registered=false;+}+}++returnerr;+}++/*+*Forthegivencpu/pmutype,walkallknownGSIs,registerthem,andadd+*themtotheresourcestructure.ReturnthenumberofGSI'scontained+*intheresstructure,andtheidofthelastCPU/PMUweadded.+*/+int__initarm_pmu_acpi_gsi_res(structpmu_types*pmus,+structresource*res,int*last_cpu_id)+{+inti,count;+intirq;++pr_info("Setting up %d PMUs for CPU type %X\n",pmus->cpu_count,+pmus->cpu_type);+/* lets group all the PMU's from similar CPU's together */+count=0;+for_each_possible_cpu(i){+structcpuinfo_arm64*cinfo=per_cpu_ptr(&cpu_data,i);++if(pmus->cpu_type==MIDR_PARTNUM(cinfo->reg_midr)){+pr_devel("Setting up CPU %d\n",i);+if(pmu_irqs[i].gsi==0)+continue;++irq=acpi_register_gsi(NULL,pmu_irqs[i].gsi,+pmu_irqs[i].trigger,+ACPI_ACTIVE_HIGH);++res[count].start=res[count].end=irq;+res[count].flags=IORESOURCE_IRQ;++if(pmu_irqs[i].trigger==ACPI_EDGE_SENSITIVE)+res[count].flags|=IORESOURCE_IRQ_HIGHEDGE;+else+res[count].flags|=IORESOURCE_IRQ_HIGHLEVEL;++pmu_irqs[i].registered=true;+count++;+(*last_cpu_id)=cinfo->reg_midr;++if(irq_is_percpu(irq))+pr_debug("PPI detected\n");+}+}+returncount;}staticint__initpmu_acpi_init(void)
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:32
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Use the newly provided acpi_pmu routines
to detect that case, and instantiate more than one set of counters.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 7 +++-
drivers/perf/arm_pmu_acpi.c | 98 ++++++++++++++++++++-------------------------
2 files changed, 50 insertions(+), 55 deletions(-)
@@ -1049,7 +1049,12 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}else{-ret=probe_plat_pmu(pmu,probe_table,read_cpuid_id());+if(acpi_disabled){+/* use the current cpu. */+ret=probe_plat_pmu(pmu,probe_table,+read_cpuid_id());+}else+ret=probe_plat_pmu(pmu,probe_table,pdev->id);}if(ret){
@@ -47,7 +52,7 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)}/* Count number and type of CPU cores in the system. */-void__initarm_pmu_acpi_determine_cpu_types(structpmu_types*pmus)+staticvoid__initarm_pmu_acpi_determine_cpu_types(structpmu_types*pmus){inti,j;
@@ -164,63 +169,48 @@ int __init arm_pmu_acpi_gsi_res(struct pmu_types *pmus,staticint__initpmu_acpi_init(void){-structplatform_device*pdev;-structpmu_irq*pirq=pmu_irqs;-structresource*res,*r;+structresource*res;interr=-ENOMEM;-inti,count,irq;+intcount;+intj,last_cpu_id;+structpmu_types*pmus;+pr_debug("Prepare registration\n");if(acpi_disabled)return0;-/* Must have irq for boot boot cpu, at least */-if(pirq->gsi==0)-return-EINVAL;--irq=acpi_register_gsi(NULL,pirq->gsi,pirq->trigger,-ACPI_ACTIVE_HIGH);--if(irq_is_percpu(irq))-count=1;-else-for(i=1,count=1;i<NR_CPUS;i++)-if(pmu_irqs[i].gsi)-++count;--pdev=platform_device_alloc(PMU_PDEV_NAME,-1);-if(!pdev)-gotoerr_free_gsi;--res=kcalloc(count,sizeof(*res),GFP_KERNEL);-if(!res)-gotoerr_free_device;--for(i=0,r=res;i<count;i++,pirq++,r++){-if(i)-irq=acpi_register_gsi(NULL,pirq->gsi,pirq->trigger,-ACPI_ACTIVE_HIGH);-r->start=r->end=irq;-r->flags=IORESOURCE_IRQ;-if(pirq->trigger==ACPI_EDGE_SENSITIVE)-r->flags|=IORESOURCE_IRQ_HIGHEDGE;-else-r->flags|=IORESOURCE_IRQ_HIGHLEVEL;-}--err=platform_device_add_resources(pdev,res,count);-if(!err)-err=platform_device_add(pdev);-kfree(res);-if(!err)-return0;--err_free_device:-platform_device_put(pdev);--err_free_gsi:-for(i=0;i<count;i++)-acpi_unregister_gsi(pmu_irqs[i].gsi);+pmus=kcalloc(num_possible_cpus(),sizeof(structpmu_types),+GFP_KERNEL);++if(pmus){+arm_pmu_acpi_determine_cpu_types(pmus);++for(j=0;pmus[j].cpu_count;j++){+pr_devel("CPU type %X, count %d\n",pmus[j].cpu_type,+pmus[j].cpu_count);+res=kcalloc(pmus[j].cpu_count,+sizeof(structresource),GFP_KERNEL);++/* for a given PMU type collect all the GSIs. */+if(res){+count=arm_pmu_acpi_gsi_res(&pmus[j],res,+&last_cpu_id);+/*+*registerthissetofinterrupts+*withanewPMUdevice+*/+err=arm_pmu_acpi_register_pmu(count,+res,+last_cpu_id);+kfree(res);+}else+pr_warn("PMU unable to allocate interrupt resource space\n");+}+kfree(pmus);+}else+pr_warn("PMU: Unable to allocate pmu count structures\n");returnerr;}+arch_initcall(pmu_acpi_init);
From: Jeremy Linton <hidden> Date: 2016-06-09 22:23:33
Update the ARM PMU file list, and add the arm mailing list.
Signed-off-by: Jeremy Linton <redacted>
---
MAINTAINERS | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
From: Will Deacon <hidden> Date: 2016-06-15 10:59:43
On Thu, Jun 09, 2016 at 05:23:26PM -0500, Jeremy Linton wrote:
From: Mark Salter <redacted>
In preparation for ACPI support, add a pmu_probe_info table to
the arm_pmu_device_probe() call. This table gets used when
probing in the absence of a devicetree node for PMU.
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 8 +++++++-
include/linux/perf/arm_pmu.h | 3 +++
2 files changed, 10 insertions(+), 1 deletion(-)
This is likely to do the wrong thing for big/little systems, so I'm not
keen on this for DT. We should mandate that the PMU is described there.
If you need this as a building block for ACPI, please ensure that the
code is only executed when booting via ACPI.
Will
From: Will Deacon <hidden> Date: 2016-06-15 11:14:49
On Thu, Jun 09, 2016 at 05:23:27PM -0500, Jeremy Linton wrote:
quoted hunk
ARMv8 machines can identify the micro/arch defined counters
that are available on a machine. Add all these counters to the
default armv8 perf map. At run-time disable the counters which
are not available on the given PMU.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 45 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)
From: Will Deacon <hidden> Date: 2016-06-15 11:33:33
On Thu, Jun 09, 2016 at 05:23:28PM -0500, Jeremy Linton wrote:
quoted hunk
From: Mark Salter <redacted>
In the case of ACPI, the PMU IRQ information is contained in the
MADT table. Also, since the PMU does not exist as a device in the
ACPI DSDT table, it is necessary to create a platform device so
that the appropriate driver probing is triggered.
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Jeremy Linton <redacted>
---
NOTE: Much of the code in pmu_acpi_init() is replaced in a later version
of this patch. The later version of the patch cleans up some of the
possible style/error handling issues that have been pointed out with
this version.
arch/arm64/kernel/smp.c | 5 +++
drivers/perf/Kconfig | 4 ++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 ++++
5 files changed, 114 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
@@ -560,6 +562,9 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)*/acpi_set_mailbox_entry(cpu_count,processor);+/* get PMU irq info */+arm_pmu_parse_acpi(cpu_count,processor);+
Nit: the outer functions are now misnomers, since this has nothing to do
with the GIC. It feels like acpi_parse_gic_cpu_interface could use some
slight restructuring so that the MADT parsing looks less confused.
This is quite tricky to read, thanks to the aliasing of pirq and
pmu_irqs[0]. Why is it necessary to register the first gsi separately,
rather than just register it later in the loop with all the other
interrupts?
Will
From: Will Deacon <hidden> Date: 2016-06-15 13:09:50
On Thu, Jun 09, 2016 at 05:23:30PM -0500, Jeremy Linton wrote:
quoted hunk
On systems with multiple PMU types the PMU to CPU affinity
needs to be detected and set. The CPU to interrupt affinity
should also be set.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 52 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 42 insertions(+), 10 deletions(-)
@@ -872,25 +874,56 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)}/*-*CPUPMUidentificationandprobing.+*CPUPMUidentificationandprobing.Itspossibletohave+*multipleCPUtypesinanARMmachine.Assurethatweare+*pickingtherightPMUtypesbasedontheCPUinquestion*/-staticintprobe_current_pmu(structarm_pmu*pmu,-conststructpmu_probe_info*info)+staticintprobe_plat_pmu(structarm_pmu*pmu,+conststructpmu_probe_info*info,+unsignedintpmuid){-intcpu=get_cpu();-unsignedintcpuid=read_cpuid_id();intret=-ENODEV;+intcpu;+intaff_ctr=0;+structplatform_device*pdev=pmu->plat_device;+intirq=platform_get_irq(pdev,0);-pr_info("probing PMU on CPU %d\n",cpu);+if(irq>=0&&!irq_is_percpu(irq)){+pmu->irq_affinity=kcalloc(pdev->num_resources,sizeof(int),+GFP_KERNEL);+if(!pmu->irq_affinity)+return-ENOMEM;+}++for_each_possible_cpu(cpu){+unsignedintcpuid=read_specific_cpuid(cpu);++if(cpuid==pmuid){+cpumask_set_cpu(cpu,&pmu->supported_cpus);+pr_devel("enable pmu on cpu %d\n",cpu);
Please remove the pr_devels that you've added (similarly elsewhere in
the patch).
You can also kill this now -- I don't think it's much use.
+ /* find the type of PMU given the CPU */
for (; info->init != NULL; info++) {
- if ((cpuid & info->mask) != info->cpuid)
+ if ((pmuid & info->mask) != info->cpuid)
continue;
+ pr_devel("Found PMU\n");
ret = info->init(pmu);
+ if (!info->cpuid) {
+ pmu->name = kasprintf(GFP_KERNEL, "%s_0x%x",
+ pmu->name, ARM_PARTNUM(pmuid));
Hmm, I'm not so keen on this. I think we should name the first PMU
"armv8_pmuv3" (i.e. pmu->name) and then any subsequent PMUs should inherit
an index, e.g. "armv8_pmuv3_1". That follows a similar style to uncore
PMUs and also keeps this backwards compatible with what's currently done
with devicetree.
You can create a 'cpumask' file in the sysfs directory that identifies
the cores for the PMU (again, there is precedent for this elsewhere).
Will
From: Will Deacon <hidden> Date: 2016-06-15 13:22:46
On Thu, Jun 09, 2016 at 05:23:32PM -0500, Jeremy Linton wrote:
quoted hunk
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Use the newly provided acpi_pmu routines
to detect that case, and instantiate more than one set of counters.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 7 +++-
drivers/perf/arm_pmu_acpi.c | 98 ++++++++++++++++++++-------------------------
2 files changed, 50 insertions(+), 55 deletions(-)
@@ -1049,7 +1049,12 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}else{-ret=probe_plat_pmu(pmu,probe_table,read_cpuid_id());+if(acpi_disabled){+/* use the current cpu. */+ret=probe_plat_pmu(pmu,probe_table,+read_cpuid_id());+}else+ret=probe_plat_pmu(pmu,probe_table,pdev->id);}if(ret){
Given that you already have dynamic allocation in here, why not use a
linked-list for the pmus list, and avoid having a potentially huge temporary
data structure?
Will
From: Jeremy Linton <hidden> Date: 2016-06-15 15:07:24
On 06/15/2016 06:33 AM, Will Deacon wrote:
On Thu, Jun 09, 2016 at 05:23:28PM -0500, Jeremy Linton wrote:
quoted
From: Mark Salter <redacted>
In the case of ACPI, the PMU IRQ information is contained in the
MADT table. Also, since the PMU does not exist as a device in the
ACPI DSDT table, it is necessary to create a platform device so
that the appropriate driver probing is triggered.
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Jeremy Linton <redacted>
---
NOTE: Much of the code in pmu_acpi_init() is replaced in a later version
of this patch. The later version of the patch cleans up some of the
possible style/error handling issues that have been pointed out with
this version.
arch/arm64/kernel/smp.c | 5 +++
drivers/perf/Kconfig | 4 ++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 ++++
5 files changed, 114 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
@@ -560,6 +562,9 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)*/acpi_set_mailbox_entry(cpu_count,processor);+/* get PMU irq info */+arm_pmu_parse_acpi(cpu_count,processor);+
Nit: the outer functions are now misnomers, since this has nothing to do
with the GIC. It feels like acpi_parse_gic_cpu_interface could use some
slight restructuring so that the MADT parsing looks less confused.
This is quite tricky to read, thanks to the aliasing of pirq and
pmu_irqs[0]. Why is it necessary to register the first gsi separately,
rather than just register it later in the loop with all the other
interrupts?
Short answer, no particular reason. If you notice patch 6,
arm_pmu_acpi_gsi_res() reworks this to register all the irqs for a
particular PMU at the same time.
From: Jeremy Linton <hidden> Date: 2016-06-15 15:21:12
On 06/15/2016 08:22 AM, Will Deacon wrote:
On Thu, Jun 09, 2016 at 05:23:32PM -0500, Jeremy Linton wrote:
quoted
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Use the newly provided acpi_pmu routines
to detect that case, and instantiate more than one set of counters.
Given that you already have dynamic allocation in here, why not use a
linked-list for the pmus list, and avoid having a potentially huge temporary
data structure?
Sure... But, its really only going to be 2 entries on any existing
system, I considered limiting this to something reasonable like "4" with
a WARN() because who will ever build a machine with more than 4
different CPU types in it? <chuckle> Is that an acceptable solution, or
do you prefer the list?
Although, maybe the list is better, because then when someone does build
such a machine it will "just work (TM)". <shrug>
From: Will Deacon <hidden> Date: 2016-06-15 15:30:07
On Wed, Jun 15, 2016 at 10:21:12AM -0500, Jeremy Linton wrote:
On 06/15/2016 08:22 AM, Will Deacon wrote:
quoted
On Thu, Jun 09, 2016 at 05:23:32PM -0500, Jeremy Linton wrote:
quoted
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Use the newly provided acpi_pmu routines
to detect that case, and instantiate more than one set of counters.
Given that you already have dynamic allocation in here, why not use a
linked-list for the pmus list, and avoid having a potentially huge temporary
data structure?
Sure... But, its really only going to be 2 entries on any existing system, I
considered limiting this to something reasonable like "4" with a WARN()
because who will ever build a machine with more than 4 different CPU types
in it? <chuckle> Is that an acceptable solution, or do you prefer the list?
I do prefer the list, just because kcalloc(num_possible_cpus(), ...) could
be pretty large, and like you say, we're likely to need 2-3 entries in
practice.
Will