Skip to content

Commit bfc653a

Browse files
bwicaksononvwilldeacon
authored andcommitted
perf: arm_cspmu: Separate Arm and vendor module
Arm Coresight PMU driver consists of main standard code and vendor backend code. Both are currently built as a single module. This patch adds vendor registration API to separate the two to keep things modular. The main driver requests each known backend module during initialization and defer device binding process. The backend module then registers an init callback to the main driver and continue the device driver binding process. Signed-off-by: Besar Wicaksono <[email protected]> Reviewed-by: Suzuki K Poulose <[email protected]> Reviewed-and-tested-by: Ilkka Koskinen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 6465e26 commit bfc653a

File tree

6 files changed

+199
-60
lines changed

6 files changed

+199
-60
lines changed

drivers/perf/arm_cspmu/Kconfig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0
22
#
3-
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44

55
config ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU
66
tristate "ARM Coresight Architecture PMU"
@@ -10,3 +10,10 @@ config ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU
1010
based on ARM CoreSight PMU architecture. Note that this PMU
1111
architecture does not have relationship with the ARM CoreSight
1212
Self-Hosted Tracing.
13+
14+
config NVIDIA_CORESIGHT_PMU_ARCH_SYSTEM_PMU
15+
tristate "NVIDIA Coresight Architecture PMU"
16+
depends on ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU
17+
help
18+
Provides NVIDIA specific attributes for performance monitoring unit
19+
(PMU) devices based on ARM CoreSight PMU architecture.

drivers/perf/arm_cspmu/Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# SPDX-License-Identifier: GPL-2.0
44

55
obj-$(CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU) += arm_cspmu_module.o
6-
arm_cspmu_module-y := arm_cspmu.o nvidia_cspmu.o
6+
arm_cspmu_module-y := arm_cspmu.o
7+
8+
obj-$(CONFIG_NVIDIA_CORESIGHT_PMU_ARCH_SYSTEM_PMU) += nvidia_cspmu.o

drivers/perf/arm_cspmu/arm_cspmu.c

Lines changed: 133 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* The user should refer to the vendor technical documentation to get details
1717
* about the supported events.
1818
*
19-
* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
19+
* Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2020
*
2121
*/
2222

@@ -26,11 +26,11 @@
2626
#include <linux/interrupt.h>
2727
#include <linux/io-64-nonatomic-lo-hi.h>
2828
#include <linux/module.h>
29+
#include <linux/mutex.h>
2930
#include <linux/perf_event.h>
3031
#include <linux/platform_device.h>
3132

3233
#include "arm_cspmu.h"
33-
#include "nvidia_cspmu.h"
3434

3535
#define PMUNAME "arm_cspmu"
3636
#define DRVNAME "arm-cs-arch-pmu"
@@ -112,11 +112,10 @@
112112
*/
113113
#define HILOHI_MAX_POLL 1000
114114

115-
/* JEDEC-assigned JEP106 identification code */
116-
#define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B
117-
118115
static unsigned long arm_cspmu_cpuhp_state;
119116

117+
static DEFINE_MUTEX(arm_cspmu_lock);
118+
120119
static struct acpi_apmt_node *arm_cspmu_apmt_node(struct device *dev)
121120
{
122121
return *(struct acpi_apmt_node **)dev_get_platdata(dev);
@@ -373,27 +372,37 @@ static struct attribute_group arm_cspmu_cpumask_attr_group = {
373372
.attrs = arm_cspmu_cpumask_attrs,
374373
};
375374

376-
struct impl_match {
377-
u32 pmiidr;
378-
u32 mask;
379-
int (*impl_init_ops)(struct arm_cspmu *cspmu);
380-
};
381-
382-
static const struct impl_match impl_match[] = {
375+
static struct arm_cspmu_impl_match impl_match[] = {
383376
{
384-
.pmiidr = ARM_CSPMU_IMPL_ID_NVIDIA,
385-
.mask = ARM_CSPMU_PMIIDR_IMPLEMENTER,
386-
.impl_init_ops = nv_cspmu_init_ops
377+
.module_name = "nvidia_cspmu",
378+
.pmiidr_val = ARM_CSPMU_IMPL_ID_NVIDIA,
379+
.pmiidr_mask = ARM_CSPMU_PMIIDR_IMPLEMENTER,
380+
.module = NULL,
381+
.impl_init_ops = NULL,
387382
},
388-
{}
383+
{0}
389384
};
390385

386+
static struct arm_cspmu_impl_match *arm_cspmu_impl_match_get(u32 pmiidr)
387+
{
388+
struct arm_cspmu_impl_match *match = impl_match;
389+
390+
for (; match->pmiidr_val; match++) {
391+
u32 mask = match->pmiidr_mask;
392+
393+
if ((match->pmiidr_val & mask) == (pmiidr & mask))
394+
return match;
395+
}
396+
397+
return NULL;
398+
}
399+
391400
static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
392401
{
393-
int ret;
402+
int ret = 0;
394403
struct arm_cspmu_impl_ops *impl_ops = &cspmu->impl.ops;
395404
struct acpi_apmt_node *apmt_node = arm_cspmu_apmt_node(cspmu->dev);
396-
const struct impl_match *match = impl_match;
405+
struct arm_cspmu_impl_match *match;
397406

398407
/*
399408
* Get PMU implementer and product id from APMT node.
@@ -405,17 +414,36 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
405414
readl(cspmu->base0 + PMIIDR);
406415

407416
/* Find implementer specific attribute ops. */
408-
for (; match->pmiidr; match++) {
409-
const u32 mask = match->mask;
417+
match = arm_cspmu_impl_match_get(cspmu->impl.pmiidr);
418+
419+
/* Load implementer module and initialize the callbacks. */
420+
if (match) {
421+
mutex_lock(&arm_cspmu_lock);
422+
423+
if (match->impl_init_ops) {
424+
/* Prevent unload until PMU registration is done. */
425+
if (try_module_get(match->module)) {
426+
cspmu->impl.module = match->module;
427+
cspmu->impl.match = match;
428+
ret = match->impl_init_ops(cspmu);
429+
if (ret)
430+
module_put(match->module);
431+
} else {
432+
WARN(1, "arm_cspmu failed to get module: %s\n",
433+
match->module_name);
434+
ret = -EINVAL;
435+
}
436+
} else {
437+
request_module_nowait(match->module_name);
438+
ret = -EPROBE_DEFER;
439+
}
410440

411-
if ((match->pmiidr & mask) == (cspmu->impl.pmiidr & mask)) {
412-
ret = match->impl_init_ops(cspmu);
413-
if (ret)
414-
return ret;
441+
mutex_unlock(&arm_cspmu_lock);
415442

416-
break;
417-
}
418-
}
443+
if (ret)
444+
return ret;
445+
} else
446+
cspmu->impl.module = THIS_MODULE;
419447

420448
/* Use default callbacks if implementer doesn't provide one. */
421449
CHECK_DEFAULT_IMPL_OPS(impl_ops, get_event_attrs);
@@ -478,11 +506,6 @@ arm_cspmu_alloc_attr_group(struct arm_cspmu *cspmu)
478506
struct attribute_group **attr_groups = NULL;
479507
struct device *dev = cspmu->dev;
480508
const struct arm_cspmu_impl_ops *impl_ops = &cspmu->impl.ops;
481-
int ret;
482-
483-
ret = arm_cspmu_init_impl_ops(cspmu);
484-
if (ret)
485-
return NULL;
486509

487510
cspmu->identifier = impl_ops->get_identifier(cspmu);
488511
cspmu->name = impl_ops->get_name(cspmu);
@@ -1149,7 +1172,7 @@ static int arm_cspmu_register_pmu(struct arm_cspmu *cspmu)
11491172

11501173
cspmu->pmu = (struct pmu){
11511174
.task_ctx_nr = perf_invalid_context,
1152-
.module = THIS_MODULE,
1175+
.module = cspmu->impl.module,
11531176
.pmu_enable = arm_cspmu_enable,
11541177
.pmu_disable = arm_cspmu_disable,
11551178
.event_init = arm_cspmu_event_init,
@@ -1196,11 +1219,17 @@ static int arm_cspmu_device_probe(struct platform_device *pdev)
11961219
if (ret)
11971220
return ret;
11981221

1199-
ret = arm_cspmu_register_pmu(cspmu);
1222+
ret = arm_cspmu_init_impl_ops(cspmu);
12001223
if (ret)
12011224
return ret;
12021225

1203-
return 0;
1226+
ret = arm_cspmu_register_pmu(cspmu);
1227+
1228+
/* Matches arm_cspmu_init_impl_ops() above. */
1229+
if (cspmu->impl.module != THIS_MODULE)
1230+
module_put(cspmu->impl.module);
1231+
1232+
return ret;
12041233
}
12051234

12061235
static int arm_cspmu_device_remove(struct platform_device *pdev)
@@ -1300,6 +1329,75 @@ static void __exit arm_cspmu_exit(void)
13001329
cpuhp_remove_multi_state(arm_cspmu_cpuhp_state);
13011330
}
13021331

1332+
int arm_cspmu_impl_register(const struct arm_cspmu_impl_match *impl_match)
1333+
{
1334+
struct arm_cspmu_impl_match *match;
1335+
int ret = 0;
1336+
1337+
match = arm_cspmu_impl_match_get(impl_match->pmiidr_val);
1338+
1339+
if (match) {
1340+
mutex_lock(&arm_cspmu_lock);
1341+
1342+
if (!match->impl_init_ops) {
1343+
match->module = impl_match->module;
1344+
match->impl_init_ops = impl_match->impl_init_ops;
1345+
} else {
1346+
/* Broken match table may contain non-unique entries */
1347+
WARN(1, "arm_cspmu backend already registered for module: %s, pmiidr: 0x%x, mask: 0x%x\n",
1348+
match->module_name,
1349+
match->pmiidr_val,
1350+
match->pmiidr_mask);
1351+
1352+
ret = -EINVAL;
1353+
}
1354+
1355+
mutex_unlock(&arm_cspmu_lock);
1356+
1357+
if (!ret)
1358+
ret = driver_attach(&arm_cspmu_driver.driver);
1359+
} else {
1360+
pr_err("arm_cspmu reg failed, unable to find a match for pmiidr: 0x%x\n",
1361+
impl_match->pmiidr_val);
1362+
1363+
ret = -EINVAL;
1364+
}
1365+
1366+
return ret;
1367+
}
1368+
EXPORT_SYMBOL_GPL(arm_cspmu_impl_register);
1369+
1370+
static int arm_cspmu_match_device(struct device *dev, const void *match)
1371+
{
1372+
struct arm_cspmu *cspmu = platform_get_drvdata(to_platform_device(dev));
1373+
1374+
return (cspmu && cspmu->impl.match == match) ? 1 : 0;
1375+
}
1376+
1377+
void arm_cspmu_impl_unregister(const struct arm_cspmu_impl_match *impl_match)
1378+
{
1379+
struct device *dev;
1380+
struct arm_cspmu_impl_match *match;
1381+
1382+
match = arm_cspmu_impl_match_get(impl_match->pmiidr_val);
1383+
1384+
if (WARN_ON(!match))
1385+
return;
1386+
1387+
/* Unbind the driver from all matching backend devices. */
1388+
while ((dev = driver_find_device(&arm_cspmu_driver.driver, NULL,
1389+
match, arm_cspmu_match_device)))
1390+
device_release_driver(dev);
1391+
1392+
mutex_lock(&arm_cspmu_lock);
1393+
1394+
match->module = NULL;
1395+
match->impl_init_ops = NULL;
1396+
1397+
mutex_unlock(&arm_cspmu_lock);
1398+
}
1399+
EXPORT_SYMBOL_GPL(arm_cspmu_impl_unregister);
1400+
13031401
module_init(arm_cspmu_init);
13041402
module_exit(arm_cspmu_exit);
13051403

drivers/perf/arm_cspmu/arm_cspmu.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-License-Identifier: GPL-2.0
22
*
33
* ARM CoreSight Architecture PMU driver.
4-
* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
* Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
55
*
66
*/
77

@@ -69,6 +69,9 @@
6969
#define ARM_CSPMU_PMIIDR_IMPLEMENTER GENMASK(11, 0)
7070
#define ARM_CSPMU_PMIIDR_PRODUCTID GENMASK(31, 20)
7171

72+
/* JEDEC-assigned JEP106 identification code */
73+
#define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B
74+
7275
struct arm_cspmu;
7376

7477
/* This tracks the events assigned to each counter in the PMU. */
@@ -106,9 +109,23 @@ struct arm_cspmu_impl_ops {
106109
struct attribute *attr, int unused);
107110
};
108111

112+
/* Vendor/implementer registration parameter. */
113+
struct arm_cspmu_impl_match {
114+
/* Backend module. */
115+
struct module *module;
116+
const char *module_name;
117+
/* PMIIDR value/mask. */
118+
u32 pmiidr_val;
119+
u32 pmiidr_mask;
120+
/* Callback to vendor backend to init arm_cspmu_impl::ops. */
121+
int (*impl_init_ops)(struct arm_cspmu *cspmu);
122+
};
123+
109124
/* Vendor/implementer descriptor. */
110125
struct arm_cspmu_impl {
111126
u32 pmiidr;
127+
struct module *module;
128+
struct arm_cspmu_impl_match *match;
112129
struct arm_cspmu_impl_ops ops;
113130
void *ctx;
114131
};
@@ -147,4 +164,10 @@ ssize_t arm_cspmu_sysfs_format_show(struct device *dev,
147164
struct device_attribute *attr,
148165
char *buf);
149166

167+
/* Register vendor backend. */
168+
int arm_cspmu_impl_register(const struct arm_cspmu_impl_match *impl_match);
169+
170+
/* Unregister vendor backend. */
171+
void arm_cspmu_impl_unregister(const struct arm_cspmu_impl_match *impl_match);
172+
150173
#endif /* __ARM_CSPMU_H__ */

drivers/perf/arm_cspmu/nvidia_cspmu.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// SPDX-License-Identifier: GPL-2.0
22
/*
3-
* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
*
55
*/
66

77
/* Support for NVIDIA specific attributes. */
88

9+
#include <linux/module.h>
910
#include <linux/topology.h>
1011

11-
#include "nvidia_cspmu.h"
12+
#include "arm_cspmu.h"
1213

1314
#define NV_PCIE_PORT_COUNT 10ULL
1415
#define NV_PCIE_FILTER_ID_MASK GENMASK_ULL(NV_PCIE_PORT_COUNT - 1, 0)
@@ -351,7 +352,7 @@ static char *nv_cspmu_format_name(const struct arm_cspmu *cspmu,
351352
return name;
352353
}
353354

354-
int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
355+
static int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
355356
{
356357
u32 prodid;
357358
struct nv_cspmu_ctx *ctx;
@@ -395,6 +396,31 @@ int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
395396

396397
return 0;
397398
}
398-
EXPORT_SYMBOL_GPL(nv_cspmu_init_ops);
399+
400+
/* Match all NVIDIA Coresight PMU devices */
401+
static const struct arm_cspmu_impl_match nv_cspmu_param = {
402+
.pmiidr_val = ARM_CSPMU_IMPL_ID_NVIDIA,
403+
.module = THIS_MODULE,
404+
.impl_init_ops = nv_cspmu_init_ops
405+
};
406+
407+
static int __init nvidia_cspmu_init(void)
408+
{
409+
int ret;
410+
411+
ret = arm_cspmu_impl_register(&nv_cspmu_param);
412+
if (ret)
413+
pr_err("nvidia_cspmu backend registration error: %d\n", ret);
414+
415+
return ret;
416+
}
417+
418+
static void __exit nvidia_cspmu_exit(void)
419+
{
420+
arm_cspmu_impl_unregister(&nv_cspmu_param);
421+
}
422+
423+
module_init(nvidia_cspmu_init);
424+
module_exit(nvidia_cspmu_exit);
399425

400426
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)