From: Jeremy Linton <hidden> Date: 2016-12-02 18:55:54
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.
v11:
- Add is_smp() check to read_specific_cpuid() for arch/arm. Update
c_show() and various routines in arm_pmu_acpi() to use the macro.
- Moved the duplicate "generic" pmu detection code into its own
patch and hoist it into arm_pmu_device_probe() so it works
for DT based systems as well.
v10:
- Rebase to 4.9
- Rework the arm_perf_start_cpu changes to support the 4.9 hotplug
changes.
- Remove the call to acpi_register_gsi() from the cpu online code path.
Instead the GSI's are registered during the initcall. This changes
the error handling a bit because we now try to clean up the
previously registered GSIs in a couple important places. This
was also a result of the rebase.
- Dropped the MIDR partnumber usage, its no longer necessary to
differentiate by only the partnum, so this helps to clarify the code
a bit.
- Shuffle some code around and rename a few variables.
- Added a few comments to hopefully clarify some questions people have
previously had about unused MADT entries, skipping processing cores
with MIDR=0, etc.
v9:
- Add/cleanup an additional hotplug patch I've had sitting around. This
patch brings the ACPI PMU mostly on par with the DT functionality with
respect to having CPUs offline during boot. This should help clarify
some of the code structuring.
- Cleanup the list of PMU types early if we fail to allocate memory for an
additional pmu type.
v8:
- Rebase to 4.8rc4
- Assorted minor comment/hunk placement/etc tweaks per Punit Agrawal
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.
Jeremy Linton (6):
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 multiple generic PMUs and append counter
arm64: pmu: Detect and enable multiple PMUs in an ACPI system
arm: pmu: Add PMU definitions for cores not initially online
Mark Salter (1):
arm64: pmu: Cache PMU interrupt numbers from MADT parse
arch/arm/include/asm/cputype.h | 4 +
arch/arm/kernel/setup.c | 2 +-
arch/arm64/include/asm/cputype.h | 3 +
arch/arm64/kernel/perf_event.c | 2 +-
arch/arm64/kernel/smp.c | 18 ++-
drivers/perf/Kconfig | 4 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu.c | 107 ++++++++++++++--
drivers/perf/arm_pmu_acpi.c | 271 +++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 12 ++
10 files changed, 404 insertions(+), 20 deletions(-)
create mode 100644 drivers/perf/arm_pmu_acpi.c
--
2.5.5
From: Jeremy Linton <hidden> Date: 2016-12-02 18:55: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-12-02 18:55:56
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 | 4 ++++
arch/arm/kernel/setup.c | 2 +-
arch/arm64/include/asm/cputype.h | 3 +++
3 files changed, 8 insertions(+), 1 deletion(-)
From: Jeremy Linton <hidden> Date: 2016-12-02 18:55:57
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. Since the platform
device creation needs to happen after the CPU's have been started, and
the MADT parsing needs to happen before, we save off the interrupt
numbers discovered during the parsing.
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 | 40 ++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 +++++++
5 files changed, 57 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
From: Jeremy Linton <hidden> Date: 2016-12-02 18:55: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 | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
@@ -889,25 +891,47 @@ 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);+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);break;}-put_cpu();returnret;}
@@ -1043,8 +1067,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-12-02 18:55:59
In heterogeneous CPU systems its likely that there are multiple
PMU types. If a system is using the generic armv8_pmuv3 rather
than a PMU with a hard-coded set of events then we want to uniquely
identify each PMU in /sys. We do this by appending an "_x" to the
pmu name. This then creates PMUs like, "armv8_pmuv3" and
"armv8_pmuv3_1", "armv8_pmuv3_2" for a system with 3 PMU types.
Signed-off-by: Jeremy Linton <redacted>
---
arch/arm64/kernel/perf_event.c | 2 +-
drivers/perf/arm_pmu.c | 20 ++++++++++++++++++++
include/linux/perf/arm_pmu.h | 1 +
3 files changed, 22 insertions(+), 1 deletion(-)
From: Jeremy Linton <hidden> Date: 2016-12-02 18:56:00
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 | 8 +-
drivers/perf/arm_pmu_acpi.c | 231 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 238 insertions(+), 1 deletion(-)
@@ -1068,7 +1068,13 @@ 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){
@@ -38,3 +49,223 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)elsepmu_irqs[cpu].trigger=ACPI_LEVEL_SENSITIVE;}++staticvoid__initarm_pmu_acpi_handle_alloc_failure(structlist_head*pmus)+{+inti;+structpmu_types*pmu,*safe_temp;++list_for_each_entry_safe(pmu,safe_temp,pmus,list){+list_del(&pmu->list);+kfree(pmu);+}++for_each_possible_cpu(i)+if(pmu_irqs[i].irq>0)+acpi_unregister_gsi(pmu_irqs[i].gsi);+}++/*+*CountnumberandtypeofCPUcoresinthesystem.Returnsthenumber+*of"unused"MADTentrieswecouldnotassociatewithaPMU.Thiscan+*betheresultofCPU'snotbeingonline,orerrorsintheMADT.+*Undernormalcircumstancesthiswillbe0.+*/+staticint__initarm_pmu_acpi_determine_cpu_types(structlist_head*pmus)+{+inti;+intunused_madt_entries=0;++for_each_possible_cpu(i){+u32reg_midr=read_specific_cpuid(i);+structpmu_types*pmu;++/*+*IgnoreGSIregistrationfailurefornow,as+*someoftheMADTentriesmaynotbeused.+*/+pmu_irqs[i].irq=acpi_register_gsi(NULL,pmu_irqs[i].gsi,+pmu_irqs[i].trigger,+ACPI_ACTIVE_HIGH);+/* likely not online */+if(reg_midr==0){+unused_madt_entries++;+continue;+}++list_for_each_entry(pmu,pmus,list){+if(pmu->cpu_type==reg_midr){+pmu->cpu_count++;+break;+}+}++/* we didn't find the CPU type, add an entry to identify it */+if(&pmu->list==pmus){+pmu=kzalloc(sizeof(structpmu_types),GFP_KERNEL);+if(!pmu){+pr_err("Unable to allocate pmu_types\n");+arm_pmu_acpi_handle_alloc_failure(pmus);+break;+}+pmu->cpu_type=reg_midr;+pmu->cpu_count++;+list_add_tail(&pmu->list,pmus);+}+}++returnunused_madt_entries;+}++staticint__initarm_pmu_acpi_register_device(intcount,structresource*res,+intcpu_id)+{+structplatform_device*pdev;+interr=-ENOMEM;++pdev=platform_device_alloc(ARMV8_PMU_PDEV_NAME,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");+platform_device_put(pdev);+}+}else{+pr_warn("Unable to allocate platform device\n");+}++returnerr;+}++staticvoid__initarm_pmu_acpi_unregister_pmu_gsi(intcpu_id)+{+inti;++for_each_possible_cpu(i){++if(read_specific_cpuid(i)==cpu_id){+pmu_irqs[i].used=false;+if(pmu_irqs[i].irq>0)+acpi_unregister_gsi(pmu_irqs[i].gsi);+pmu_irqs[i].gsi=-ENODEV;+}+}+}++/*+*RegistersthegroupofPMUinterfaceswhichcorrespondtothe'cpu_id'.+*Thisgrouputilizes'count'resourcesinthe'res'.+*/+staticint__initarm_pmu_acpi_register_pmu(intcount,structresource*res,+intcpu_id)+{+interr;++err=arm_pmu_acpi_register_device(count,res,cpu_id);++/* unmark and unregister GSIs for this PMU */+if(err)+arm_pmu_acpi_unregister_pmu_gsi(cpu_id);++returnerr;+}++intarm_pmu_acpi_retrieve_irq(structresource*res,intcpu)+{+intirq=-ENODEV;++if(pmu_irqs[cpu].used){+pr_info("CPU %d's interrupt is already used\n",cpu);+}else{+pmu_irqs[cpu].used=true;+res->start=pmu_irqs[cpu].irq;+res->end=pmu_irqs[cpu].irq;+res->flags=IORESOURCE_IRQ;+if(pmu_irqs[cpu].trigger==ACPI_EDGE_SENSITIVE)+res->flags|=IORESOURCE_IRQ_HIGHEDGE;+else+res->flags|=IORESOURCE_IRQ_HIGHLEVEL;+}+returnirq;+}++/*+*Forthegivencpu/pmutype,walkallknownGSIs,registerthem,andadd+*themtotheresourcestructure.ReturnthenumberofGSI'scontained+*intheresstructure,andtheidofthelastCPU/PMUweadded.+*/+staticint__initarm_pmu_acpi_gsi_res(structpmu_types*pmus,+structresource*res)+{+inti,count;++/* lets group all the PMU's from similar CPU's together */+count=0;+for_each_possible_cpu(i){+u32reg_midr=read_specific_cpuid(i);++if(pmus->cpu_type==reg_midr){+if((pmu_irqs[i].gsi==0)&&(reg_midr!=0))+continue;++/* likely not online */+if(!reg_midr)+continue;++arm_pmu_acpi_retrieve_irq(&res[count],i);+count++;+}+}+returncount;+}++staticint__initpmu_acpi_register(structpmu_types*pmu)+{+intcount;+interr=-ENOMEM;+structresource*res;++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);+/* register this set of interrupts with a new PMU device */+err=arm_pmu_acpi_register_pmu(pmu->cpu_count,res,+pmu->cpu_type);+if(!err)+pr_info("Register %d devices for %X\n",count,+pmu->cpu_type);+kfree(res);+}else{+pr_warn("PMU unable to allocate interrupt resource\n");+arm_pmu_acpi_unregister_pmu_gsi(pmu->cpu_type);+}+returnerr;+}++staticint__initpmu_acpi_init(void)+{+structpmu_types*pmu,*safe_temp;+boolunused_madt_entries;+LIST_HEAD(pmus);++if(acpi_disabled)+return0;++unused_madt_entries=arm_pmu_acpi_determine_cpu_types(&pmus);++list_for_each_entry_safe(pmu,safe_temp,&pmus,list){+pmu->cpu_count+=unused_madt_entries;+pmu_acpi_register(pmu);++list_del(&pmu->list);+kfree(pmu);+}++return0;+}++arch_initcall(pmu_acpi_init);
From: Jeremy Linton <hidden> Date: 2016-12-02 18:56:01
ACPI CPUs aren't associated with a PMU until they have been put
online. This means that we potentially have to update a PMU
definition the first time a CPU is hot added to the machine.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 38 ++++++++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 4 ++++
2 files changed, 40 insertions(+), 2 deletions(-)
From: Will Deacon <hidden> Date: 2016-12-06 15:21:18
On Fri, Dec 02, 2016 at 12:56:01PM -0600, Jeremy Linton wrote:
quoted hunk
ACPI CPUs aren't associated with a PMU until they have been put
online. This means that we potentially have to update a PMU
definition the first time a CPU is hot added to the machine.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 38 ++++++++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 4 ++++
2 files changed, 40 insertions(+), 2 deletions(-)
From: Jeremy Linton <hidden> Date: 2016-12-06 17:56:56
Hi,
On 12/06/2016 09:21 AM, Will Deacon wrote:
On Fri, Dec 02, 2016 at 12:56:01PM -0600, Jeremy Linton wrote:
quoted
ACPI CPUs aren't associated with a PMU until they have been put
online. This means that we potentially have to update a PMU
definition the first time a CPU is hot added to the machine.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 38 ++++++++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 4 ++++
2 files changed, 40 insertions(+), 2 deletions(-)
Why do you need this spinlock? The hotplug notifiers are serialised afaik,
and you don't take it anywhere else.
Well, I assumed they were serialized, but then I went looking for a
guarantee and couldn't find one specific to the notifiers, even though
the previous lock was removed. Admittedly, I didn't spend too long
looking, but there is a piece missing...
Which is the sync between the hotplug notification and perf start/stop.
By itself that extends this lock into the consumers of the resource
structure. Which might not be the right choice because even without
these ACPI specific bits, simply running a few cpus online/offline while
simultaneously doing something like `perf stat -e cache-misses ls &` in
a loop causes deadlocks/crashes.
That problem doesn't appear to be specific to the ACPI/PMU so I've
stayed away from it in this patch set, although potentially a larger fix
might cover this as well.
On Tue, Dec 06, 2016 at 11:56:56AM -0600, Jeremy Linton wrote:
Hi,
Which might not be the right choice because
even without these ACPI specific bits, simply running a few cpus
online/offline while simultaneously doing something like `perf stat
-e cache-misses ls &` in a loop causes deadlocks/crashes.
That problem doesn't appear to be specific to the ACPI/PMU so I've
stayed away from it in this patch set, although potentially a larger
fix might cover this as well.
Urrgh; I can reproduce lockups on Seattle with v4.9-rc8.
I'll look into that.
If you see any more issues in this area, please report them in a new
thread.
Thanks,
Mark.
From: Will Deacon <hidden> Date: 2016-12-15 17:08:11
On Tue, Dec 06, 2016 at 11:56:56AM -0600, Jeremy Linton wrote:
Hi,
On 12/06/2016 09:21 AM, Will Deacon wrote:
quoted
On Fri, Dec 02, 2016 at 12:56:01PM -0600, Jeremy Linton wrote:
quoted
ACPI CPUs aren't associated with a PMU until they have been put
online. This means that we potentially have to update a PMU
definition the first time a CPU is hot added to the machine.
Signed-off-by: Jeremy Linton <redacted>
---
drivers/perf/arm_pmu.c | 38 ++++++++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 4 ++++
2 files changed, 40 insertions(+), 2 deletions(-)
Why do you need this spinlock? The hotplug notifiers are serialised afaik,
and you don't take it anywhere else.
Well, I assumed they were serialized, but then I went looking for a
guarantee and couldn't find one specific to the notifiers, even though the
previous lock was removed.
They should be serialised either by virtue of them all running off the back
of a single CPU (because the hotplug thread hasn't yet been created), or
by the st->done completion for the hotplug work threads.
Will