From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:51
v7:
Rebase to 4.8rc3
Remove cpu affinity sysfs entry. While providing a CPU mask for
ARMv8 PMU's is really helpful in big/little environments, reworking the PMU
code to support the cpumask attribute for !arm64 PMUs is out of the scope
of this patch set.
Fix CPU miscount problem where an alloc failure followed by successfully
allocating the structure can result in under counting the CPUs associated
with the PMU. This bug was created in v6 with the conversion to a linked list.
Remove initial platform device creation code by Mark Salter, and re-squash
multiple platform device creation code together with helper routines.
Other minor tweakage.
v6:
Added cpu affinity sysfs entry
Converted pmu_types array, to linked list
Restrict use of the armv8_pmu_probe_table to ACPI systems
Rename MADT parsing routines in smp.c
Convert sysfs PMU name to use index rather than partnum
Remove pr_devel statements
Other Minor cleanups
Add Partial Ack-by Will Deacon
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 (7):
arm64: pmu: Probe default hw/cache counters
arm64: pmu: Hoist pmu platform device name
arm64: Rename the common MADT parse routine
arm: arm64: Add routine to determine cpuid of other cpus
arm: arm64: pmu: Assign platform PMU CPU affinity
arm64: pmu: Detect and 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 | 2 +
arch/arm64/include/asm/cputype.h | 3 +
arch/arm64/kernel/perf_event.c | 58 ++++++++++-
arch/arm64/kernel/smp.c | 18 ++--
drivers/perf/Kconfig | 4 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu.c | 60 +++++++++--
drivers/perf/arm_pmu_acpi.c | 215 +++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 12 +++
10 files changed, 353 insertions(+), 23 deletions(-)
create mode 100644 drivers/perf/arm_pmu_acpi.c
--
2.5.5
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:52
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 | 13 ++++++++++++-
drivers/perf/arm_pmu.c | 2 +-
include/linux/perf/arm_pmu.h | 3 +++
3 files changed, 16 insertions(+), 2 deletions(-)
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:53
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>
Acked-by: Will Deacon <redacted>
---
arch/arm64/kernel/perf_event.c | 45 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)
@@ -906,9 +925,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)
@@ -1045,8 +1077,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-08-23 20:47:54
Move the PMU name into a common header file so it may
be referenced by other users.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 2 +-
include/linux/perf/arm_pmu.h | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:55
The MADT parser in smp.c is now being used to parse
out NUMA, PMU and ACPI parking protocol information as
well as the GIC information for which it was originally
created. Rename it to avoid a misleading name.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/smp.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:56
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>
---
arch/arm64/kernel/smp.c | 5 +++++
drivers/perf/Kconfig | 4 ++++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 ++++++
5 files changed, 68 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:57
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 | 2 ++
arch/arm64/include/asm/cputype.h | 3 +++
2 files changed, 5 insertions(+)
From: Jeremy Linton <hidden> Date: 2016-08-23 20:47:58
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 | 53 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 43 insertions(+), 10 deletions(-)
@@ -876,25 +878,57 @@ 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;+staticintduplicate_pmus;+structplatform_device*pdev=pmu->plat_device;+intirq=platform_get_irq(pdev,0);++if(irq>=0&&!irq_is_percpu(irq)){+pmu->irq_affinity=kcalloc(pdev->num_resources,sizeof(int),+GFP_KERNEL);+if(!pmu->irq_affinity)+return-ENOMEM;+}-pr_info("probing PMU on CPU %d\n",cpu);+for_each_possible_cpu(cpu){+unsignedintcpuid=read_specific_cpuid(cpu);+if(cpuid==pmuid){+cpumask_set_cpu(cpu,&pmu->supported_cpus);+if(pmu->irq_affinity){+pmu->irq_affinity[aff_ctr]=cpu;+aff_ctr++;+}+}+}++/* 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;ret=info->init(pmu);+if((!info->cpuid)&&(duplicate_pmus)){+pmu->name=kasprintf(GFP_KERNEL,"%s_%d",+pmu->name,duplicate_pmus);+if(!pmu->name){+kfree(pmu->irq_affinity);+ret=-ENOMEM;+}+}+duplicate_pmus++;break;}-put_cpu();returnret;}
@@ -1029,8 +1063,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}elseif(probe_table){-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-08-23 20:47:59
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Iterate the CPU's and make a determination
about how many of each type exist in the system. Then take and create
a PMU platform device for each type, and assign it the interrupts parsed
from the MADT. Creating a platform device is necessary because the PMUs
are not described as devices in the DSDT table.
This code is loosely based on earlier work by Mark Salter.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 7 +-
drivers/perf/arm_pmu_acpi.c | 164 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 170 insertions(+), 1 deletion(-)
@@ -1063,7 +1063,12 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}elseif(probe_table){-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){
@@ -39,13 +49,167 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)pmu_irqs[cpu].trigger=ACPI_LEVEL_SENSITIVE;}+/* Count number and type of CPU cores in the system. */+staticvoid__initarm_pmu_acpi_determine_cpu_types(structlist_head*pmus)+{+inti;+boolalloc_failure=false;++for_each_possible_cpu(i){+structcpuinfo_arm64*cinfo=per_cpu_ptr(&cpu_data,i);+u32partnum=MIDR_PARTNUM(cinfo->reg_midr);+structpmu_types*pmu;++list_for_each_entry(pmu,pmus,list){+if(pmu->cpu_type==partnum){+pmu->cpu_count++;+break;+}+}++/* we didn't find the CPU type, add an entry to identify it */+if((&pmu->list==pmus)&&(!alloc_failure)){+pmu=kzalloc(sizeof(structpmu_types),GFP_KERNEL);+if(!pmu){+pr_warn("Unable to allocate pmu_types\n");+/*+*continuetocountcpusforanypmu_types+*alreadyallocated,butdon'tallocateany+*morepmu_types.Thisavoidsundercounting.+*/+alloc_failure=true;+}else{+pmu->cpu_type=partnum;+pmu->cpu_count++;+list_add_tail(&pmu->list,pmus);+}+}+}+}++/*+*RegistersthegroupofPMUinterfaceswhichcorrespondtothe'last_cpu_id'.+*Thisgrouputilizes'count'resourcesinthe'res'.+*/+staticint__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(ARMV8_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.+*/+staticint__initarm_pmu_acpi_gsi_res(structpmu_types*pmus,+structresource*res,int*last_cpu_id)+{+inti,count;+intirq;++/* 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)){+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;+}+}+returncount;+}+staticint__initpmu_acpi_init(void){+structresource*res;interr=-ENOMEM;+intcount,cpu_id;+structpmu_types*pmu,*safe_temp;+LIST_HEAD(pmus);if(acpi_disabled)return0;+arm_pmu_acpi_determine_cpu_types(&pmus);++list_for_each_entry_safe(pmu,safe_temp,&pmus,list){+res=kcalloc(pmu->cpu_count,+sizeof(structresource),GFP_KERNEL);++/* for a given PMU type collect all the GSIs. */+if(res){+count=arm_pmu_acpi_gsi_res(pmu,res,+&cpu_id);+/*+*registerthissetofinterrupts+*withanewPMUdevice+*/+err=arm_pmu_acpi_register_pmu(count,res,cpu_id);+if(!err)+pr_info("Registered %d devices for %X\n",+count,pmu->cpu_type);+kfree(res);+}else+pr_warn("PMU unable to allocate interrupt resource space\n");++list_del(&pmu->list);+kfree(pmu);+}+returnerr;}+arch_initcall(pmu_acpi_init);
From: Jeremy Linton <hidden> Date: 2016-08-23 20:48:00
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(-)
Hi Jeremy,
One comment below.
Jeremy Linton [off-list ref] writes:
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>
---
Hi Jeremy,
A few comments below.
Jeremy Linton [off-list ref] writes:
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.
The commit title and message are not true after the re-organisation from
v6. We don't create the platform devices until Patch 8. Maybe something
along the lines of -
"parse the core PMU interrupts from MADT"
More comments below.
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 | 53 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 43 insertions(+), 10 deletions(-)
@@ -876,25 +878,57 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu) } /*- * CPU PMU identification and probing.+ * CPU PMU identification and probing. Its possible to have+ * multiple CPU types in an ARM machine. Assure that we are+ * picking the right PMU types based on the CPU in question */-static int probe_current_pmu(struct arm_pmu *pmu,- const struct pmu_probe_info *info)+static int probe_plat_pmu(struct arm_pmu *pmu,+ const struct pmu_probe_info *info,+ unsigned int pmuid) {- int cpu = get_cpu();- unsigned int cpuid = read_cpuid_id(); int ret = -ENODEV;+ int cpu;+ int aff_ctr = 0;+ static int duplicate_pmus;+ struct platform_device *pdev = pmu->plat_device;+ int irq = platform_get_irq(pdev, 0);++ if (irq >= 0 && !irq_is_percpu(irq)) {+ pmu->irq_affinity = kcalloc(pdev->num_resources, sizeof(int),+ GFP_KERNEL);+ if (!pmu->irq_affinity)+ return -ENOMEM;+ }- pr_info("probing PMU on CPU %d\n", cpu);+ for_each_possible_cpu(cpu) {+ unsigned int cpuid = read_specific_cpuid(cpu);+ if (cpuid == pmuid) {+ cpumask_set_cpu(cpu, &pmu->supported_cpus);+ if (pmu->irq_affinity) {+ pmu->irq_affinity[aff_ctr] = cpu;+ aff_ctr++;+ }+ }+ }++ /* 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; ret = info->init(pmu);+ if ((!info->cpuid) && (duplicate_pmus)) {
It's not obvious what the condition "!info->cpuid" implies. Please add a
comment explaining the rationale.
Also, the parenthesis around the two parts of the logical operation can
be dropped.
Its possible that an ACPI system has multiple CPU types in it
with differing PMU counters. Iterate the CPU's and make a determination
about how many of each type exist in the system. Then take and create
a PMU platform device for each type, and assign it the interrupts parsed
from the MADT. Creating a platform device is necessary because the PMUs
are not described as devices in the DSDT table.
This code is loosely based on earlier work by Mark Salter.
Signed-off-by: Jeremy Linton <redacted>
Thanks for squashing changes to arm_pmu_acpi.c from different patches in
v6 into one patch. Except for the a function definition in Patch 5 that can
be moved here I think you've got everything. The combined patch is a lot
easier to review.
Some comments below.
@@ -1063,7 +1063,12 @@ int arm_pmu_device_probe(struct platform_device *pdev,if(!ret)ret=init_fn(pmu);}elseif(probe_table){-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);
Please add matching braces on both sides of the else.
@@ -39,13 +49,167 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic) pmu_irqs[cpu].trigger = ACPI_LEVEL_SENSITIVE; }+/* Count number and type of CPU cores in the system. */+static void __init arm_pmu_acpi_determine_cpu_types(struct list_head *pmus)+{+ int i;+ bool alloc_failure = false;++ for_each_possible_cpu(i) {+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, i);+ u32 partnum = MIDR_PARTNUM(cinfo->reg_midr);+ struct pmu_types *pmu;++ list_for_each_entry(pmu, pmus, list) {+ if (pmu->cpu_type == partnum) {+ pmu->cpu_count++;+ break;+ }+ }++ /* we didn't find the CPU type, add an entry to identify it */+ if ((&pmu->list == pmus) && (!alloc_failure)) {
The parenthesis around the conditions can be dropped.
+ pmu = kzalloc(sizeof(struct pmu_types), GFP_KERNEL);
+ if (!pmu) {
+ pr_warn("Unable to allocate pmu_types\n");
+ /*
+ * continue to count cpus for any pmu_types
+ * already allocated, but don't allocate any
+ * more pmu_types. This avoids undercounting.
+ */
+ alloc_failure = true;
Why not just fail probe and return an error? What is the benefit of
having some of the PMUs available?
+ } else {
+ pmu->cpu_type = partnum;
+ pmu->cpu_count++;
+ list_add_tail(&pmu->list, pmus);
+ }
+ }
+ }
+}
+
+/*
+ * Registers the group of PMU interfaces which correspond to the 'last_cpu_id'.
+ * This group utilizes 'count' resources in the 'res'.
+ */
+static int __init arm_pmu_acpi_register_pmu(int count, struct resource *res,
+ int last_cpu_id)
Please drop the prefix "last_". AFAICS, it doesn't provide any
information.
+{
+ int i;
+ int err = -ENOMEM;
+ bool free_gsi = false;
+ struct platform_device *pdev;
+
+ if (count) {
+ pdev = platform_device_alloc(ARMV8_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;
+ }
+ }
This entire "if" block is quite hard to review.
Quoting Documentation/CodingStyle, "if you need more than 3 levels of
indentation, you're screwed anyway, and should fix your program."
Moving the for_each_possible_cpu block out of this function should help
makes things simpler. It doesn't have any connection to registering the
platform device and you could then do
if (!count)
return count;
which should help reduce a level of indentation. But you can further use
the same approach with other conditions in the block as well.
+
+ return err;
+}
+
+/*
+ * For the given cpu/pmu type, walk all known GSIs, register them, and add
+ * them to the resource structure. Return the number of GSI's contained
+ * in the res structure, and the id of the last CPU/PMU we added.
+ */
+static int __init arm_pmu_acpi_gsi_res(struct pmu_types *pmus,
+ struct resource *res, int *last_cpu_id)
+{
+ int i, count;
+ int irq;
+
+ /* lets group all the PMU's from similar CPU's together */
+ count = 0;
+ for_each_possible_cpu(i) {
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, i);
+
+ if (pmus->cpu_type == MIDR_PARTNUM(cinfo->reg_midr)) {
+ if (pmu_irqs[i].gsi == 0)
+ continue;
Please don't silently continue if the irq is missing. It deserves a user
visible message. We don't want users complaining about kernel issues
when the firmware fails to provide the required information.
What is the benefit of using the entire MIDR for cpu_id when the
grouping is done on the basis of a subset, i.e., part number.
+ }
+ }
+ return count;
+}
+
static int __init pmu_acpi_init(void)
{
+ struct resource *res;
int err = -ENOMEM;
+ int count, cpu_id;
+ struct pmu_types *pmu, *safe_temp;
+ LIST_HEAD(pmus);
if (acpi_disabled)
return 0;
+ arm_pmu_acpi_determine_cpu_types(&pmus);
+
+ list_for_each_entry_safe(pmu, safe_temp, &pmus, list) {
+ res = kcalloc(pmu->cpu_count,
+ sizeof(struct resource), GFP_KERNEL);
+
+ /* for a given PMU type collect all the GSIs. */
+ if (res) {
+ count = arm_pmu_acpi_gsi_res(pmu, res,
+ &cpu_id);
+ /*
+ * register this set of interrupts
+ * with a new PMU device
+ */
+ err = arm_pmu_acpi_register_pmu(count, res, cpu_id);
+ if (!err)
+ pr_info("Registered %d devices for %X\n",
+ count, pmu->cpu_type);
+ kfree(res);
+ } else
+ pr_warn("PMU unable to allocate interrupt resource space\n");
Same comment about partial registration as above. It's better to error
out IMO.
Also if this stays, please use matching parenthesis on both sides of the else block.
Thanks,
Punit
From: Jeremy Linton <hidden> Date: 2016-08-26 22:44:59
Hi,
On 08/26/2016 10:04 AM, Punit Agrawal wrote:
(trimming)
quoted
+ pmu = kzalloc(sizeof(struct pmu_types), GFP_KERNEL);
+ if (!pmu) {
+ pr_warn("Unable to allocate pmu_types\n");
+ /*
+ * continue to count cpus for any pmu_types
+ * already allocated, but don't allocate any
+ * more pmu_types. This avoids undercounting.
+ */
+ alloc_failure = true;
Why not just fail probe and return an error? What is the benefit of
having some of the PMUs available?
AFAIC, there isn't a good reason for penalizing PMU's which we can get
working if a subset of the system PMUs can't be created. But this is per
PMU type, so with current systems the kzalloc will be called a max of 2
times (there is the potential of a 3rd time, due to some other error
handling, but that doesn't change the argument much). AKA, this doesn't
result in "partial registration" of a PMU.
So, really the only question in my mind is does it work if one of the
allocations fails and the other succeeds, and the answer is yes, the
remaining interrupts/etc get associated with the correct PMU, and it
gets created and should work as well as perf currently works in systems
with heterogeneous PMUs (cue discussion about CPU process migration).
So, since this is early boot, and we are taking a tiny allocation, if
this fails i suspect that the machine will probably die anyway, not due
to the choice of whether the PMU is counted properly or not. I would
guess the platform allocation or similar will die..
(trimming)
quoted
+/*
+ * For the given cpu/pmu type, walk all known GSIs, register them, and add
+ * them to the resource structure. Return the number of GSI's contained
+ * in the res structure, and the id of the last CPU/PMU we added.
+ */
+static int __init arm_pmu_acpi_gsi_res(struct pmu_types *pmus,
+ struct resource *res, int *last_cpu_id)
+{
+ int i, count;
+ int irq;
+
+ /* lets group all the PMU's from similar CPU's together */
+ count = 0;
+ for_each_possible_cpu(i) {
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, i);
+
+ if (pmus->cpu_type == MIDR_PARTNUM(cinfo->reg_midr)) {
+ if (pmu_irqs[i].gsi == 0)
+ continue;
Please don't silently continue if the irq is missing. It deserves a user
visible message. We don't want users complaining about kernel issues
when the firmware fails to provide the required information.
Check for the return value of acpi_register_gsi as it can return an
error.
Ok, so this is probably a little subtle, but IIRC from a few months back
when I was testing this/reworking it, duplicate GSI registrations for
exclusive PPI's result in errors (see any random discussion about PPI's
not being ACPI GSI's). As the code to handle SPI vs PPI exists in the
platform code, I decided to ignore registration errors until such a
determination can be made. AKA, i'm potentially tossing invalid irq's
into the irq list for PPIs, but it doesn't matter because they are
temporary ignored. The core ARM PMU code, has a much better handle on
what is a correct interrupt binding, so the decision about whether these
failures need to be worried about are delayed until then. This results
in a large simplification because we handle the irq deregistration
necessarily for any further errors together, AKA you will notice a
complete lack of differing code paths for PPI vs SPI in this module.
As far as gsi=0, in cases where there are placeholder GICC entries in
the MADT (think disabled CPU) then i've seen gsi=0. I'm not sure that is
incorrect, so again it gets skipped, the pmu mask doesn't have it
listed, and everything "works". So if i'm going to add a message here
i'm, going to wrap it in a reg_midr!=0 check.
What is the benefit of using the entire MIDR for cpu_id when the
grouping is done on the basis of a subset, i.e., part number.
Because the platform code isn't partnum specific, unless the the
ARMV8_PMU_PART_PROBE() macro is utilized. Delaying any decisions about
what part of the MIDR happens to be used (if any) to the part probe
table is probably a good idea. Especially if someone happens to think
that they want a ARMV8_PMU_PART_REVISION_PROBE() or similar macro..
If anything It might be worth removing the partnum checks in this module
as well. That way should someone ship a machine with the same CPU only
differing by revision (or pick your favorite !partnum field) they get
different PMUs definitions. Why anyone would do that I cannot guess, but
I do see that apparently the xscale version was important at one point.
From: Will Deacon <hidden> Date: 2016-09-01 14:30:37
On Fri, Aug 26, 2016 at 05:44:59PM -0500, Jeremy Linton wrote:
Hi,
On 08/26/2016 10:04 AM, Punit Agrawal wrote:
(trimming)
quoted
quoted
+ pmu = kzalloc(sizeof(struct pmu_types), GFP_KERNEL);
+ if (!pmu) {
+ pr_warn("Unable to allocate pmu_types\n");
+ /*
+ * continue to count cpus for any pmu_types
+ * already allocated, but don't allocate any
+ * more pmu_types. This avoids undercounting.
+ */
+ alloc_failure = true;
Why not just fail probe and return an error? What is the benefit of
having some of the PMUs available?
AFAIC, there isn't a good reason for penalizing PMU's which we can get
working if a subset of the system PMUs can't be created. But this is per PMU
type, so with current systems the kzalloc will be called a max of 2 times
(there is the potential of a 3rd time, due to some other error handling, but
that doesn't change the argument much). AKA, this doesn't result in "partial
registration" of a PMU.
... but this will look mighty confusing to userspace, where things will
appear to "half-work", if for some reason the machine makes it that far
at all.
I think we should stick with the KISS approach and just fail the probe
as Punit is suggesting.
Will