This repository has been archived by the owner on Jul 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf: arm_cmn: improve and make it work on 2P.
There are following fixes: 1. If DTC node was discovered before other nodes, the old code would register pmu events before discovery finish which causing missing events in pmu sys tree. 2. CPU hotplug state needs to be initialized per driver, not per instance. 3. Use ACPI UID to create unique name for each instance. Signed-off-by: Tuan Phan <[email protected]>
- Loading branch information
1 parent
19a1b38
commit c6f079f
Showing
1 changed file
with
78 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,7 +104,6 @@ | |
//TODO: Is it worth probing dt_dbg_id.num_pmucntr? | ||
#define CMN_DT_NUM_COUNTERS 8 | ||
|
||
|
||
enum cmn_node_type { | ||
CMN_TYPE_INVALID, | ||
CMN_TYPE_DVM, | ||
|
@@ -126,6 +125,7 @@ enum cmn_node_type { | |
struct arm_cmn { | ||
struct device *dev; | ||
void __iomem *base; | ||
void __iomem *dtc_base; | ||
|
||
u8 mesh_x; | ||
u8 mesh_y; | ||
|
@@ -170,6 +170,9 @@ struct arm_cmn_dtc { | |
|
||
#define to_cmn_dtc(x) container_of(x, struct arm_cmn_dtc, pmu) | ||
|
||
/* Keep track of our dynamic hotplug state */ | ||
static enum cpuhp_state arm_cmn_cpuhp_state; | ||
|
||
struct arm_cmn_event_attr { | ||
struct device_attribute attr; | ||
enum cmn_node_type type; | ||
|
@@ -765,20 +768,28 @@ static irqreturn_t arm_cmn_irq_handler(int irq, void *dev_id) | |
return res; | ||
} | ||
|
||
static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) | ||
static int arm_cmn_init_pmu(struct arm_cmn *cmn) | ||
{ | ||
struct platform_device *pdev = to_platform_device(cmn->dev); | ||
struct arm_cmn_dtc *dtc; | ||
unsigned long long value; | ||
acpi_handle handle; | ||
acpi_status status; | ||
const char *name; | ||
int irq, err; | ||
|
||
if (!cmn->dtc_base) { | ||
dev_err(cmn->dev, "no DTC found\n"); | ||
return -ENODEV; | ||
} | ||
|
||
dtc = devm_kzalloc(cmn->dev, sizeof(*dtc), GFP_KERNEL); | ||
if (!dtc) | ||
return -ENOMEM; | ||
|
||
irq = platform_get_irq(pdev, id); | ||
irq = platform_get_irq(pdev, 0); | ||
if (irq <= 0) { | ||
dev_err(cmn->dev, "missing IRQ for DTC %d\n", id); | ||
dev_err(cmn->dev, "missing IRQ for DTC\n"); | ||
return -EINVAL; | ||
} | ||
|
||
|
@@ -788,8 +799,10 @@ static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) | |
if (err) | ||
return err; | ||
|
||
platform_set_drvdata(pdev, dtc); | ||
|
||
dtc->cmn = cmn; | ||
dtc->base = base; | ||
dtc->base = cmn->dtc_base; | ||
dtc->cpu = get_cpu(); | ||
dtc->pmu = (struct pmu) { | ||
.attr_groups = arm_cmn_attr_groups, | ||
|
@@ -804,21 +817,35 @@ static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) | |
.read = arm_cmn_event_read, | ||
}; | ||
|
||
if (id == 0) { | ||
name = "arm_cmn"; | ||
handle = ACPI_HANDLE(cmn->dev); | ||
if (handle) { | ||
status = acpi_evaluate_integer(handle, METHOD_NAME__UID, NULL, | ||
&value); | ||
if (ACPI_FAILURE(status)) { | ||
dev_err(cmn->dev, | ||
"Failed to evaluate _UID (0x%x)\n", status); | ||
return -ENODEV; | ||
} | ||
|
||
name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", | ||
(unsigned int)value); | ||
} else { | ||
name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", id); | ||
if (!name) | ||
return -ENOMEM; | ||
/* FIXME: multiple instance if no ACPI? */ | ||
name = "arm_cmn"; | ||
} | ||
|
||
cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CMN_ONLINE, | ||
&dtc->cpuhp_node); | ||
put_cpu(); | ||
|
||
writel_relaxed(CMN_DT_PMCR_PMU_EN, base + CMN_DT_PMCR); | ||
writel_relaxed(CMN_DT_PMCR_PMU_EN, dtc->base + CMN_DT_PMCR); | ||
|
||
err = perf_pmu_register(&dtc->pmu, name, -1); | ||
if (err) | ||
cpuhp_state_remove_instance(arm_cmn_cpuhp_state, | ||
&dtc->cpuhp_node); | ||
|
||
return perf_pmu_register(&dtc->pmu, name, -1); | ||
return err; | ||
} | ||
|
||
static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lvl) | ||
|
@@ -884,8 +911,8 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lv | |
"multiple DTCs not supported; events outside domain 0 will not be counted correctly\n"); | ||
return 0; | ||
} | ||
// FIXME: node_logid is apparently not a nice simple index | ||
return arm_cmn_init_pmu(cmn, region, node_logid); | ||
cmn->dtc_base = region; | ||
return 0; | ||
/* These guys have PMU events */ | ||
case CMN_TYPE_DVM: | ||
case CMN_TYPE_HNI: | ||
|
@@ -895,6 +922,10 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lv | |
case CMN_TYPE_RND: | ||
case CMN_TYPE_CXRA: | ||
case CMN_TYPE_CXHA: | ||
if (node_type == CMN_TYPE_RND) { | ||
/* RND uses the same event type with RNI */ | ||
node_type = CMN_TYPE_RNI; | ||
} | ||
node = &cmn->dns[cmn->num_dns++]; | ||
break; | ||
/* Nothing to see here */ | ||
|
@@ -1030,7 +1061,6 @@ static int arm_cmn_probe(struct platform_device *pdev) | |
return -ENOMEM; | ||
|
||
cmn->dev = &pdev->dev; | ||
platform_set_drvdata(pdev, cmn); | ||
|
||
if (has_acpi_companion(cmn->dev)) | ||
rootnode = arm_cmn_acpi_probe(pdev, cmn); | ||
|
@@ -1045,17 +1075,16 @@ static int arm_cmn_probe(struct platform_device *pdev) | |
|
||
arm_cmn_mesh_fixup(cmn); | ||
|
||
return cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CMN_ONLINE, | ||
"perf/arm/cmn:online", NULL, | ||
arm_cmn_pmu_offline_cpu); | ||
return arm_cmn_init_pmu(cmn); | ||
} | ||
|
||
static int arm_cmn_remove(struct platform_device *pdev) | ||
{ | ||
//TODO: What's the neatest way to find the DTCs and clean them up? | ||
|
||
cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CMN_ONLINE); | ||
struct arm_cmn_dtc *dtc = platform_get_drvdata(pdev); | ||
|
||
//TODO: What's the neatest way to find the DTCs and clean them up? | ||
cpuhp_state_remove_instance(arm_cmn_cpuhp_state, | ||
&dtc->cpuhp_node); | ||
return 0; | ||
} | ||
|
||
|
@@ -1082,7 +1111,34 @@ static struct platform_driver arm_cmn_driver = { | |
.probe = arm_cmn_probe, | ||
.remove = arm_cmn_remove, | ||
}; | ||
module_platform_driver(arm_cmn_driver); | ||
|
||
static int __init arm_cmn_init(void) | ||
{ | ||
int ret; | ||
|
||
ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CMN_ONLINE, | ||
"perf/arm/cmn:online", NULL, | ||
arm_cmn_pmu_offline_cpu); | ||
if (ret < 0) | ||
return ret; | ||
|
||
arm_cmn_cpuhp_state = ret; | ||
|
||
ret = platform_driver_register(&arm_cmn_driver); | ||
if (ret) | ||
cpuhp_remove_multi_state(arm_cmn_cpuhp_state); | ||
|
||
return ret; | ||
} | ||
|
||
static void __exit arm_cmn_exit(void) | ||
{ | ||
platform_driver_unregister(&arm_cmn_driver); | ||
cpuhp_remove_multi_state(arm_cmn_cpuhp_state); | ||
} | ||
|
||
module_init(arm_cmn_init); | ||
module_exit(arm_cmn_exit); | ||
|
||
MODULE_AUTHOR("Robin Murphy <[email protected]>"); | ||
MODULE_DESCRIPTION("Arm CMN-600 PMU driver"); | ||
|