Skip to contentSkip to conversation

Cavium ThunderX uncore PMU support

PATCH Superseded Jan Glauber 4 participants 14 messages Oct 29, 2016 – Nov 17, 2016

Subsystems (2)

v4 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v4 cover Cavium ThunderX uncore PMU support

As discussed, changed perf_sw_context -> perf_invalid_context.

Not changed:
- Stick to NUMA node ID to detect the socket a device belongs to but made uncore depend on CONFIG_NUMA.
- Stick to initcall for uncore framework because it is easier to do the scanning for the same type of PCI devices, also I don't know if the PCI layer would allow for several drivers to register for the same device ID.

Patches are against 4.9.0-rc2.

Changes to v3:
- use perf_invalid_context

Changes to v2:
- Embedded struct pmu and killed uncore->type
- Simplified add functions
- Unified functions where possible into a common implementation
- Use arrays to translate non-contiguous counter addresses to event_id's visible to the user
- Sorted includes
- Got rid of division for previous counter values
- Removed unneeded WARN_ONs
- Use sizeof(*ptr)
- Use bool for event_valid return
- Fixed HES_STOPPED logic
- Added some design notes and improved (hopefully) comments
- Removed pass1 counter support for now
- Merged EVENT_ATTR and EVENT_PTR defines into one (unreadable) thing
- Use pmu_enable|disable to start|stop the OCX TLK counter set
- Moved cpumask into thunder_uncore struct
- Switched to new cpuhp stuff. I still don't care about the CPU location used to access an uncore device, it may cross the CCPI and we'll pay a performance penalty. We might optimize this later, for now I feel it is not worth the time optimizing it.

--------------------------

Jan Glauber (5):
arm64: perf: Basic uncore counter support for Cavium ThunderX SOC
arm64: perf: Cavium ThunderX L2C TAD uncore support
arm64: perf: Cavium ThunderX L2C CBC uncore support
arm64: perf: Cavium ThunderX LMC uncore support
arm64: perf: Cavium ThunderX OCX TLK uncore support

 drivers/perf/Kconfig                        |  13 +
 drivers/perf/Makefile                       |   1 +
 drivers/perf/uncore/Makefile                |   5 +
 drivers/perf/uncore/uncore_cavium.c         | 355 ++++++++++++++++++++++++++
 drivers/perf/uncore/uncore_cavium.h         |  75 ++++++
 drivers/perf/uncore/uncore_cavium_l2c_cbc.c | 148 +++++++++++
 drivers/perf/uncore/uncore_cavium_l2c_tad.c | 379 ++++++++++++++++++++++++++++
 drivers/perf/uncore/uncore_cavium_lmc.c     | 118 +++++++++
 drivers/perf/uncore/uncore_cavium_ocx_tlk.c | 344 +++++++++++++++++++++++++
 include/linux/cpuhotplug.h                  |   1 +
 10 files changed, 1439 insertions(+)
 create mode 100644 drivers/perf/uncore/Makefile
 create mode 100644 drivers/perf/uncore/uncore_cavium.c
 create mode 100644 drivers/perf/uncore/uncore_cavium.h
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_cbc.c
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_tad.c
 create mode 100644 drivers/perf/uncore/uncore_cavium_lmc.c
 create mode 100644 drivers/perf/uncore/uncore_cavium_ocx_tlk.c
Signature · 1 line
2.9.0.rc0.21.g7777322
#1 lore raw

PATCH v4 1/5 arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Provide "uncore" facilities for different non-CPU performance counter units.

The uncore PMUs can be found under /sys/bus/event_source/devices. All counters are exported via sysfs in the corresponding events files under the PMU directory so the perf tool can list the event names.

There are some points that are special in this implementation:

1) The PMU detection relies on PCI device detection. If a matching PCI device is found the PMU is created. The code can deal with multiple units of the same type, e.g. more than one memory controller.

2) Counters are summarized across different units of the same type on one NUMA node but not across NUMA nodes.
For instance L2C TAD 0..7 are presented as a single counter (adding the values from TAD 0 to 7). Although losing the ability to read a single value the merged values are easier to use.

3) The counters are not CPU related. A random CPU is picked regardless of the NUMA node. There is a small performance penalty for accessing counters on a remote note but reading a performance counter is a slow operation anyway.

---
 drivers/perf/Kconfig                |  13 ++
 drivers/perf/Makefile               |   1 +
 drivers/perf/uncore/Makefile        |   1 +
 drivers/perf/uncore/uncore_cavium.c | 351 ++++++++++++++++++++++++++++++++++++
 drivers/perf/uncore/uncore_cavium.h |  71 ++++++++
 include/linux/cpuhotplug.h          |   1 +
 6 files changed, 438 insertions(+)
 create mode 100644 drivers/perf/uncore/Makefile
 create mode 100644 drivers/perf/uncore/uncore_cavium.c
 create mode 100644 drivers/perf/uncore/uncore_cavium.h
drivers/perf/Kconfig +13 -0
index 4d5c5f9..3266c87 100644
@@ -19,4 +19,17 @@ config XGENE_PMU
         help
           Say y if you want to use APM X-Gene SoC performance monitors.
 
+config UNCORE_PMU
+	bool
+
+config UNCORE_PMU_CAVIUM
+	depends on PERF_EVENTS && NUMA && ARM64
+	bool "Cavium uncore PMU support"
+	select UNCORE_PMU
+	default y
+	help
+	  Say y if you want to access performance counters of subsystems
+	  on a Cavium SOC like cache controller, memory controller or
+	  processor interconnect.
+
 endmenu
drivers/perf/Makefile +1 -0
index b116e98..d6c02c9 100644
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-y += uncore/
drivers/perf/uncore/Makefile new file +1 -0
new file mode 100644
index 0000000..6130e18
@@ -0,0 +1 @@
+obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o
drivers/perf/uncore/uncore_cavium.c new file +351 -0

351 lines not on this page · raw message

drivers/perf/uncore/uncore_cavium.h new file +71 -0

71 lines not on this page · raw message

include/linux/cpuhotplug.h +1 -0
index afe641c..973f2bb 100644
@@ -118,6 +118,7 @@ enum cpuhp_state {
 	CPUHP_AP_PERF_ARM_CCI_ONLINE,
 	CPUHP_AP_PERF_ARM_CCN_ONLINE,
 	CPUHP_AP_PERF_ARM_L2X0_ONLINE,
+	CPUHP_AP_UNCORE_CAVIUM_ONLINE,
 	CPUHP_AP_WORKQUEUE_ONLINE,
 	CPUHP_AP_RCUTREE_ONLINE,
 	CPUHP_AP_NOTIFY_ONLINE,
Signature · 1 line
2.9.0.rc0.21.g7777322
#2 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Hi Jan,

Thanks for posting an updated series. I have a few minor comments, which we can hopefully address in time for 4.10.

Also, have you run the perf fuzzer with this series applied?

https://github.com/deater/perf_event_tests

(build the tests and look under the fuzzer/ directory for the tool)

Quoted 25 lines · Jan Glauber

Provide "uncore" facilities for different non-CPU performance counter units.

The uncore PMUs can be found under /sys/bus/event_source/devices. All counters are exported via sysfs in the corresponding events files under the PMU directory so the perf tool can list the event names.

There are some points that are special in this implementation:

1) The PMU detection relies on PCI device detection. If a matching PCI device is found the PMU is created. The code can deal with multiple units of the same type, e.g. more than one memory controller.

2) Counters are summarized across different units of the same type on one NUMA node but not across NUMA nodes.
For instance L2C TAD 0..7 are presented as a single counter (adding the values from TAD 0 to 7). Although losing the ability to read a single value the merged values are easier to use.

3) The counters are not CPU related. A random CPU is picked regardless of the NUMA node. There is a small performance penalty for accessing counters on a remote note but reading a performance counter is a slow operation anyway.

Quoted 7 lines
---
 drivers/perf/Kconfig                |  13 ++
 drivers/perf/Makefile               |   1 +
 drivers/perf/uncore/Makefile        |   1 +
 drivers/perf/uncore/uncore_cavium.c | 351 ++++++++++++++++++++++++++++++++++++
 drivers/perf/uncore/uncore_cavium.h |  71 ++++++++

We already have "uncore" PMUs under drivers/perf, so I'd prefer that we renamed this a bit to reflect better what's going on. How about:

drivers/perf/cavium/

and then

drivers/perf/cavium/uncore_thunder.[ch]

?

Quoted 11 lines
 include/linux/cpuhotplug.h          |   1 +
 6 files changed, 438 insertions(+)
 create mode 100644 drivers/perf/uncore/Makefile
 create mode 100644 drivers/perf/uncore/uncore_cavium.c
 create mode 100644 drivers/perf/uncore/uncore_cavium.h
drivers/perf/Kconfig +0 -0
index 4d5c5f9..3266c87 100644

drivers/perf/Kconfig config XGENE_PMU · line 23

Quoted 5 lines
drivers/perf/Kconfig +2 -0
@@ -19,4 +19,17 @@ config XGENE_PMU
         help
           Say y if you want to use APM X-Gene SoC performance monitors.
 
+config UNCORE_PMU
+	bool

This isn't needed.

drivers/perf/Kconfig config XGENE_PMU · line 27

Quoted 4 lines
drivers/perf/Kconfig +4 -0
+
+config UNCORE_PMU_CAVIUM
+	depends on PERF_EVENTS && NUMA && ARM64
+	bool "Cavium uncore PMU support"

Please mentioned Thunder somewhere, since that's the SoC being supported.

Quoted 55 lines
drivers/perf/Kconfig +7 -0
+	select UNCORE_PMU
+	default y
+	help
+	  Say y if you want to access performance counters of subsystems
+	  on a Cavium SOC like cache controller, memory controller or
+	  processor interconnect.
+
 endmenu
drivers/perf/Makefile +1 -0
index b116e98..d6c02c9 100644
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-y += uncore/
drivers/perf/uncore/Makefile new file +1 -0
new file mode 100644
index 0000000..6130e18
@@ -0,0 +1 @@
+obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o
drivers/perf/uncore/uncore_cavium.c new file +26 -0
new file mode 100644
index 0000000..a7b4277
@@ -0,0 +1,351 @@
+/*
+ * Cavium Thunder uncore PMU support.
+ *
+ * Copyright (C) 2015,2016 Cavium Inc.
+ * Author: Jan Glauber <[email protected]>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/numa.h>
+#include <linux/slab.h>
+
+#include "uncore_cavium.h"
+
+/*
+ * Some notes about the various counters supported by this "uncore" PMU
+ * and the design:
+ *
+ * All counters are 64 bit long.
+ * There are no overflow interrupts.
+ * Counters are summarized per node/socket.
+ * Most devices appear as separate PCI devices per socket with the exception
+ * of OCX TLK which appears as one PCI device per socket and contains several
+ * units with counters that are merged.
+ * Some counters are selected via a control register (L2C TAD) and read by
+ * a number of counter registers, others (L2C CBC, LMC & OCX TLK) have
+ * one dedicated counter per event.

drivers/perf/uncore/uncore_cavium.c line 36

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+ * Some counters are not stoppable (L2C CBC & LMC).
+ * Some counters are read-only (LMC).
+ * All counters belong to PCI devices, the devices may have additional
+ * drivers but we assume we are the only user of the counter registers.
+ * We map the whole PCI BAR so we must be careful to forbid access to
+ * addresses that contain neither counters nor counter control registers.
+ */
+
+void thunder_uncore_read(struct perf_event *event)
+{

Rather than have a bunch of global symbols that are called from the individual drivers, why don't you pass a struct of function pointers to their respective init functions and keep the internals private?

Quoted 21 lines
drivers/perf/uncore/uncore_cavium.c new file +21 -0
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 prev, delta, new = 0;
+
+	node = get_node(hwc->config, uncore);
+
+	/* read counter values from all units on the node */
+	list_for_each_entry(unit, &node->unit_list, entry)
+		new += readq(hwc->event_base + unit->map);
+
+	prev = local64_read(&hwc->prev_count);
+	local64_set(&hwc->prev_count, new);
+	delta = new - prev;
+	local64_add(delta, &event->count);
+}
+
+int thunder_uncore_add(struct perf_event *event, int flags, u64 config_base,
+		       u64 event_base)
+{

drivers/perf/uncore/uncore_cavium.c line 67

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int id;
+
+	node = get_node(hwc->config, uncore);
+	id = get_id(hwc->config);
+
+	if (!cmpxchg(&node->events[id], NULL, event))
+		hwc->idx = id;

Does this need to be a full-fat cmpxchg? Who are you racing with?

drivers/perf/uncore/uncore_cavium.c line 70

Quoted 3 lines
drivers/perf/uncore/uncore_cavium.c new file +3 -0
+
+	if (hwc->idx == -1)
+		return -EBUSY;

This would be much clearer as an else statement after the cmpxchg.

Quoted 133 lines
drivers/perf/uncore/uncore_cavium.c new file +133 -0
+
+	hwc->config_base = config_base;
+	hwc->event_base = event_base;
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (flags & PERF_EF_START)
+		uncore->pmu.start(event, PERF_EF_RELOAD);
+
+	return 0;
+}
+
+void thunder_uncore_del(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int i;
+
+	event->pmu->stop(event, PERF_EF_UPDATE);
+
+	/*
+	 * For programmable counters we need to check where we installed it.
+	 * To keep this function generic always test the more complicated
+	 * case (free running counters won't need the loop).
+	 */
+	node = get_node(hwc->config, uncore);
+	for (i = 0; i < node->num_counters; i++) {
+		if (cmpxchg(&node->events[i], event, NULL) == event)
+			break;
+	}
+	hwc->idx = -1;
+}
+
+void thunder_uncore_start(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 new = 0;
+
+	/* read counter values from all units on the node */
+	node = get_node(hwc->config, uncore);
+	list_for_each_entry(unit, &node->unit_list, entry)
+		new += readq(hwc->event_base + unit->map);
+	local64_set(&hwc->prev_count, new);
+
+	hwc->state = 0;
+	perf_event_update_userpage(event);
+}
+
+void thunder_uncore_stop(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	hwc->state |= PERF_HES_STOPPED;
+
+	if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+		thunder_uncore_read(event);
+		hwc->state |= PERF_HES_UPTODATE;
+	}
+}
+
+int thunder_uncore_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore *uncore;
+
+	if (event->attr.type != event->pmu->type)
+		return -ENOENT;
+
+	/* we do not support sampling */
+	if (is_sampling_event(event))
+		return -EINVAL;
+
+	/* counters do not have these bits */
+	if (event->attr.exclude_user	||
+	    event->attr.exclude_kernel	||
+	    event->attr.exclude_host	||
+	    event->attr.exclude_guest	||
+	    event->attr.exclude_hv	||
+	    event->attr.exclude_idle)
+		return -EINVAL;
+
+	uncore = to_uncore(event->pmu);
+	if (!uncore)
+		return -ENODEV;
+	if (!uncore->event_valid(event->attr.config & UNCORE_EVENT_ID_MASK))
+		return -EINVAL;
+
+	/* check NUMA node */
+	node = get_node(event->attr.config, uncore);
+	if (!node) {
+		pr_debug("Invalid NUMA node selected\n");
+		return -EINVAL;
+	}
+
+	hwc->config = event->attr.config;
+	hwc->idx = -1;
+	return 0;
+}
+
+static ssize_t thunder_uncore_attr_show_cpumask(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct pmu *pmu = dev_get_drvdata(dev);
+	struct thunder_uncore *uncore =
+		container_of(pmu, struct thunder_uncore, pmu);
+
+	return cpumap_print_to_pagebuf(true, buf, &uncore->active_mask);
+}
+static DEVICE_ATTR(cpumask, S_IRUGO, thunder_uncore_attr_show_cpumask, NULL);
+
+static struct attribute *thunder_uncore_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+struct attribute_group thunder_uncore_attr_group = {
+	.attrs = thunder_uncore_attrs,
+};
+
+ssize_t thunder_events_sysfs_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *page)
+{
+	struct perf_pmu_events_attr *pmu_attr =
+		container_of(attr, struct perf_pmu_events_attr, attr);
+
+	if (pmu_attr->event_str)
+		return sprintf(page, "%s", pmu_attr->event_str);

drivers/perf/uncore/uncore_cavium.c line 213

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+
+	return 0;
+}
+
+/* node attribute depending on number of NUMA nodes */
+static ssize_t node_show(struct device *dev, struct device_attribute *attr,
+			 char *page)
+{
+	if (NODES_SHIFT)
+		return sprintf(page, "config:16-%d\n", 16 + NODES_SHIFT - 1);

If NODES_SHIFT is 1, you'll end up with "config:16-16", which might confuse userspace.

Quoted 8 lines
drivers/perf/uncore/uncore_cavium.c new file +8 -0
+	else
+		return sprintf(page, "config:16\n");
+}
+
+struct device_attribute format_attr_node = __ATTR_RO(node);
+
+/*
+ * Thunder uncore events are independent from CPUs. Provide a cpumask

drivers/perf/uncore/uncore_cavium.c line 231

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+ * nevertheless to prevent perf from adding the event per-cpu and just
+ * set the mask to one online CPU. Use the same cpumask for all uncore
+ * devices.
+ *
+ * There is a performance penalty for accessing a device from a CPU on
+ * another socket, but we do not care (yet).
+ */
+static int thunder_uncore_offline_cpu(unsigned int old_cpu, struct hlist_node *node)
+{
+	struct thunder_uncore *uncore = hlist_entry_safe(node, struct thunder_uncore, node);

Why _safe?

Quoted 26 lines
drivers/perf/uncore/uncore_cavium.c new file +26 -0
+	int new_cpu;
+
+	if (!cpumask_test_and_clear_cpu(old_cpu, &uncore->active_mask))
+		return 0;
+	new_cpu = cpumask_any_but(cpu_online_mask, old_cpu);
+	if (new_cpu >= nr_cpu_ids)
+		return 0;
+	perf_pmu_migrate_context(&uncore->pmu, old_cpu, new_cpu);
+	cpumask_set_cpu(new_cpu, &uncore->active_mask);
+	return 0;
+}
+
+static struct thunder_uncore_node * __init alloc_node(struct thunder_uncore *uncore,
+						      int node_id, int counters)
+{
+	struct thunder_uncore_node *node;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return NULL;
+	node->num_counters = counters;
+	INIT_LIST_HEAD(&node->unit_list);
+	return node;
+}
+
+int __init thunder_uncore_setup(struct thunder_uncore *uncore, int device_id,

drivers/perf/uncore/uncore_cavium.c line 267

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+				struct pmu *pmu, int counters)
+{
+	unsigned int vendor_id = PCI_VENDOR_ID_CAVIUM;
+	struct thunder_uncore_unit  *unit, *tmp;
+	struct thunder_uncore_node *node;
+	struct pci_dev *pdev = NULL;
+	int ret, node_id, found = 0;
+
+	/* detect PCI devices */
+	while ((pdev = pci_get_device(vendor_id, device_id, pdev))) {

Redundant brackets?

drivers/perf/uncore/uncore_cavium.c line 269

Quoted 2 lines
drivers/perf/uncore/uncore_cavium.c new file +2 -0
+		if (!pdev)
+			break;

Redundant check?

Quoted 41 lines
drivers/perf/uncore/uncore_cavium.c new file +41 -0
+		node_id = dev_to_node(&pdev->dev);
+
+		/* allocate node if necessary */
+		if (!uncore->nodes[node_id])
+			uncore->nodes[node_id] = alloc_node(uncore, node_id, counters);
+
+		node = uncore->nodes[node_id];
+		if (!node) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		unit = kzalloc(sizeof(*unit), GFP_KERNEL);
+		if (!unit) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		unit->pdev = pdev;
+		unit->map = ioremap(pci_resource_start(pdev, 0),
+				    pci_resource_len(pdev, 0));
+		list_add(&unit->entry, &node->unit_list);
+		node->nr_units++;
+		found++;
+	}
+
+	if (!found)
+		return -ENODEV;
+
+	cpuhp_state_add_instance_nocalls(CPUHP_AP_UNCORE_CAVIUM_ONLINE,
+                                         &uncore->node);
+
+	/*
+	 * perf PMU is CPU dependent in difference to our uncore devices.
+	 * Just pick a CPU and migrate away if it goes offline.
+	 */
+	cpumask_set_cpu(smp_processor_id(), &uncore->active_mask);
+
+	uncore->pmu = *pmu;
+	ret = perf_pmu_register(&uncore->pmu, uncore->pmu.name, -1);
+	if (ret)

drivers/perf/uncore/uncore_cavium.c line 321

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+		goto fail;
+
+	return 0;
+
+fail:
+	node_id = 0;
+	while (uncore->nodes[node_id]) {
+		node = uncore->nodes[node_id];
+
+		list_for_each_entry_safe(unit, tmp, &node->unit_list, entry) {

Why do you need the _safe variant?

Will

#3 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Hi Will,

thanks for the review!

Quoted 6 lines · Will Deacon

Hi Jan,

Thanks for posting an updated series. I have a few minor comments, which we can hopefully address in time for 4.10.

Also, have you run the perf fuzzer with this series applied?

No, that's new to me. Will try it.

Quoted 41 lines

https://github.com/deater/perf_event_tests

(build the tests and look under the fuzzer/ directory for the tool)

Quoted 25 lines · Jan Glauber

Provide "uncore" facilities for different non-CPU performance counter units.

The uncore PMUs can be found under /sys/bus/event_source/devices. All counters are exported via sysfs in the corresponding events files under the PMU directory so the perf tool can list the event names.

There are some points that are special in this implementation:

1) The PMU detection relies on PCI device detection. If a matching PCI device is found the PMU is created. The code can deal with multiple units of the same type, e.g. more than one memory controller.

2) Counters are summarized across different units of the same type on one NUMA node but not across NUMA nodes.
For instance L2C TAD 0..7 are presented as a single counter (adding the values from TAD 0 to 7). Although losing the ability to read a single value the merged values are easier to use.

3) The counters are not CPU related. A random CPU is picked regardless of the NUMA node. There is a small performance penalty for accessing counters on a remote note but reading a performance counter is a slow operation anyway.

Quoted 7 lines
---
 drivers/perf/Kconfig                |  13 ++
 drivers/perf/Makefile               |   1 +
 drivers/perf/uncore/Makefile        |   1 +
 drivers/perf/uncore/uncore_cavium.c | 351 ++++++++++++++++++++++++++++++++++++
 drivers/perf/uncore/uncore_cavium.h |  71 ++++++++

We already have "uncore" PMUs under drivers/perf, so I'd prefer that we renamed this a bit to reflect better what's going on. How about:

Quoted 7 lines

drivers/perf/cavium/

and then

drivers/perf/cavium/uncore_thunder.[ch]

?

OK, agreed.

Quoted 14 lines
Quoted 14 lines
 include/linux/cpuhotplug.h          |   1 +
 6 files changed, 438 insertions(+)
 create mode 100644 drivers/perf/uncore/Makefile
 create mode 100644 drivers/perf/uncore/uncore_cavium.c
 create mode 100644 drivers/perf/uncore/uncore_cavium.h
drivers/perf/Kconfig +0 -0
index 4d5c5f9..3266c87 100644
@@ -19,4 +19,17 @@ config XGENE_PMU
         help
           Say y if you want to use APM X-Gene SoC performance monitors.
 
Quoted 4 lines

drivers/perf/Kconfig config XGENE_PMU · line 23

Quoted 2 lines
drivers/perf/Kconfig +2 -0
+config UNCORE_PMU
+	bool

This isn't needed.

I thought about a symbol for all uncore pmus. But when drivers/perf is already that it can be removed.

Quoted 6 lines

drivers/perf/Kconfig config XGENE_PMU · line 27

Quoted 4 lines
drivers/perf/Kconfig +4 -0
+
+config UNCORE_PMU_CAVIUM
+	depends on PERF_EVENTS && NUMA && ARM64
+	bool "Cavium uncore PMU support"

Please mentioned Thunder somewhere, since that's the SoC being supported.

OK.

Quoted 61 lines
Quoted 61 lines
drivers/perf/Kconfig +7 -0
+	select UNCORE_PMU
+	default y
+	help
+	  Say y if you want to access performance counters of subsystems
+	  on a Cavium SOC like cache controller, memory controller or
+	  processor interconnect.
+
 endmenu
drivers/perf/Makefile +1 -0
index b116e98..d6c02c9 100644
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-y += uncore/
drivers/perf/uncore/Makefile new file +1 -0
new file mode 100644
index 0000000..6130e18
@@ -0,0 +1 @@
+obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o
drivers/perf/uncore/uncore_cavium.c new file +32 -0
new file mode 100644
index 0000000..a7b4277
@@ -0,0 +1,351 @@
+/*
+ * Cavium Thunder uncore PMU support.
+ *
+ * Copyright (C) 2015,2016 Cavium Inc.
+ * Author: Jan Glauber <[email protected]>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/numa.h>
+#include <linux/slab.h>
+
+#include "uncore_cavium.h"
+
+/*
+ * Some notes about the various counters supported by this "uncore" PMU
+ * and the design:
+ *
+ * All counters are 64 bit long.
+ * There are no overflow interrupts.
+ * Counters are summarized per node/socket.
+ * Most devices appear as separate PCI devices per socket with the exception
+ * of OCX TLK which appears as one PCI device per socket and contains several
+ * units with counters that are merged.
+ * Some counters are selected via a control register (L2C TAD) and read by
+ * a number of counter registers, others (L2C CBC, LMC & OCX TLK) have
+ * one dedicated counter per event.
+ * Some counters are not stoppable (L2C CBC & LMC).
+ * Some counters are read-only (LMC).
+ * All counters belong to PCI devices, the devices may have additional
+ * drivers but we assume we are the only user of the counter registers.
+ * We map the whole PCI BAR so we must be careful to forbid access to
+ * addresses that contain neither counters nor counter control registers.
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 36

Quoted 4 lines
drivers/perf/uncore/uncore_cavium.c new file +4 -0
+ */
+
+void thunder_uncore_read(struct perf_event *event)
+{

Rather than have a bunch of global symbols that are called from the individual drivers, why don't you pass a struct of function pointers to their respective init functions and keep the internals private?

Most of these functions are already in struct pmu. What I can do is set the shared functions in thunder_uncore_setup as default, and only override them as needed (like thunder_uncore_read_ocx_tlk) or the other way around (use default if not set already). Then I can get rid of them.

Quoted 25 lines
Quoted 25 lines
drivers/perf/uncore/uncore_cavium.c new file +25 -0
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 prev, delta, new = 0;
+
+	node = get_node(hwc->config, uncore);
+
+	/* read counter values from all units on the node */
+	list_for_each_entry(unit, &node->unit_list, entry)
+		new += readq(hwc->event_base + unit->map);
+
+	prev = local64_read(&hwc->prev_count);
+	local64_set(&hwc->prev_count, new);
+	delta = new - prev;
+	local64_add(delta, &event->count);
+}
+
+int thunder_uncore_add(struct perf_event *event, int flags, u64 config_base,
+		       u64 event_base)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int id;
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 67

Quoted 6 lines
drivers/perf/uncore/uncore_cavium.c new file +6 -0
+
+	node = get_node(hwc->config, uncore);
+	id = get_id(hwc->config);
+
+	if (!cmpxchg(&node->events[id], NULL, event))
+		hwc->idx = id;

Does this need to be a full-fat cmpxchg? Who are you racing with?

Just copy'n'paste from the existing drivers. I guess it can be relaxed.

Quoted 5 lines

drivers/perf/uncore/uncore_cavium.c line 70

Quoted 3 lines
drivers/perf/uncore/uncore_cavium.c new file +3 -0
+
+	if (hwc->idx == -1)
+		return -EBUSY;

This would be much clearer as an else statement after the cmpxchg.

Agreed.

Quoted 138 lines
Quoted 138 lines
drivers/perf/uncore/uncore_cavium.c new file +138 -0
+
+	hwc->config_base = config_base;
+	hwc->event_base = event_base;
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (flags & PERF_EF_START)
+		uncore->pmu.start(event, PERF_EF_RELOAD);
+
+	return 0;
+}
+
+void thunder_uncore_del(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int i;
+
+	event->pmu->stop(event, PERF_EF_UPDATE);
+
+	/*
+	 * For programmable counters we need to check where we installed it.
+	 * To keep this function generic always test the more complicated
+	 * case (free running counters won't need the loop).
+	 */
+	node = get_node(hwc->config, uncore);
+	for (i = 0; i < node->num_counters; i++) {
+		if (cmpxchg(&node->events[i], event, NULL) == event)
+			break;
+	}
+	hwc->idx = -1;
+}
+
+void thunder_uncore_start(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 new = 0;
+
+	/* read counter values from all units on the node */
+	node = get_node(hwc->config, uncore);
+	list_for_each_entry(unit, &node->unit_list, entry)
+		new += readq(hwc->event_base + unit->map);
+	local64_set(&hwc->prev_count, new);
+
+	hwc->state = 0;
+	perf_event_update_userpage(event);
+}
+
+void thunder_uncore_stop(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	hwc->state |= PERF_HES_STOPPED;
+
+	if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+		thunder_uncore_read(event);
+		hwc->state |= PERF_HES_UPTODATE;
+	}
+}
+
+int thunder_uncore_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore *uncore;
+
+	if (event->attr.type != event->pmu->type)
+		return -ENOENT;
+
+	/* we do not support sampling */
+	if (is_sampling_event(event))
+		return -EINVAL;
+
+	/* counters do not have these bits */
+	if (event->attr.exclude_user	||
+	    event->attr.exclude_kernel	||
+	    event->attr.exclude_host	||
+	    event->attr.exclude_guest	||
+	    event->attr.exclude_hv	||
+	    event->attr.exclude_idle)
+		return -EINVAL;
+
+	uncore = to_uncore(event->pmu);
+	if (!uncore)
+		return -ENODEV;
+	if (!uncore->event_valid(event->attr.config & UNCORE_EVENT_ID_MASK))
+		return -EINVAL;
+
+	/* check NUMA node */
+	node = get_node(event->attr.config, uncore);
+	if (!node) {
+		pr_debug("Invalid NUMA node selected\n");
+		return -EINVAL;
+	}
+
+	hwc->config = event->attr.config;
+	hwc->idx = -1;
+	return 0;
+}
+
+static ssize_t thunder_uncore_attr_show_cpumask(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct pmu *pmu = dev_get_drvdata(dev);
+	struct thunder_uncore *uncore =
+		container_of(pmu, struct thunder_uncore, pmu);
+
+	return cpumap_print_to_pagebuf(true, buf, &uncore->active_mask);
+}
+static DEVICE_ATTR(cpumask, S_IRUGO, thunder_uncore_attr_show_cpumask, NULL);
+
+static struct attribute *thunder_uncore_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+struct attribute_group thunder_uncore_attr_group = {
+	.attrs = thunder_uncore_attrs,
+};
+
+ssize_t thunder_events_sysfs_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *page)
+{
+	struct perf_pmu_events_attr *pmu_attr =
+		container_of(attr, struct perf_pmu_events_attr, attr);
+
+	if (pmu_attr->event_str)
+		return sprintf(page, "%s", pmu_attr->event_str);
+
+	return 0;
+}
+
+/* node attribute depending on number of NUMA nodes */
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 213

Quoted 5 lines
drivers/perf/uncore/uncore_cavium.c new file +5 -0
+static ssize_t node_show(struct device *dev, struct device_attribute *attr,
+			 char *page)
+{
+	if (NODES_SHIFT)
+		return sprintf(page, "config:16-%d\n", 16 + NODES_SHIFT - 1);

If NODES_SHIFT is 1, you'll end up with "config:16-16", which might confuse userspace.

So should I use "config:16" in that case? Is it OK to use this also for NODES_SHIFT=0 ?

Quoted 12 lines
Quoted 12 lines
drivers/perf/uncore/uncore_cavium.c new file +12 -0
+	else
+		return sprintf(page, "config:16\n");
+}
+
+struct device_attribute format_attr_node = __ATTR_RO(node);
+
+/*
+ * Thunder uncore events are independent from CPUs. Provide a cpumask
+ * nevertheless to prevent perf from adding the event per-cpu and just
+ * set the mask to one online CPU. Use the same cpumask for all uncore
+ * devices.
+ *
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 231

Quoted 6 lines
drivers/perf/uncore/uncore_cavium.c new file +6 -0
+ * There is a performance penalty for accessing a device from a CPU on
+ * another socket, but we do not care (yet).
+ */
+static int thunder_uncore_offline_cpu(unsigned int old_cpu, struct hlist_node *node)
+{
+	struct thunder_uncore *uncore = hlist_entry_safe(node, struct thunder_uncore, node);

Why _safe?

Not required, will remove.

Quoted 30 lines
Quoted 30 lines
drivers/perf/uncore/uncore_cavium.c new file +30 -0
+	int new_cpu;
+
+	if (!cpumask_test_and_clear_cpu(old_cpu, &uncore->active_mask))
+		return 0;
+	new_cpu = cpumask_any_but(cpu_online_mask, old_cpu);
+	if (new_cpu >= nr_cpu_ids)
+		return 0;
+	perf_pmu_migrate_context(&uncore->pmu, old_cpu, new_cpu);
+	cpumask_set_cpu(new_cpu, &uncore->active_mask);
+	return 0;
+}
+
+static struct thunder_uncore_node * __init alloc_node(struct thunder_uncore *uncore,
+						      int node_id, int counters)
+{
+	struct thunder_uncore_node *node;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return NULL;
+	node->num_counters = counters;
+	INIT_LIST_HEAD(&node->unit_list);
+	return node;
+}
+
+int __init thunder_uncore_setup(struct thunder_uncore *uncore, int device_id,
+				struct pmu *pmu, int counters)
+{
+	unsigned int vendor_id = PCI_VENDOR_ID_CAVIUM;
+	struct thunder_uncore_unit  *unit, *tmp;
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 267

Quoted 6 lines
drivers/perf/uncore/uncore_cavium.c new file +6 -0
+	struct thunder_uncore_node *node;
+	struct pci_dev *pdev = NULL;
+	int ret, node_id, found = 0;
+
+	/* detect PCI devices */
+	while ((pdev = pci_get_device(vendor_id, device_id, pdev))) {

Redundant brackets?

OK

Quoted 4 lines

drivers/perf/uncore/uncore_cavium.c line 269

Quoted 2 lines
drivers/perf/uncore/uncore_cavium.c new file +2 -0
+		if (!pdev)
+			break;

Redundant check?

OK

Quoted 45 lines
Quoted 45 lines
drivers/perf/uncore/uncore_cavium.c new file +45 -0
+		node_id = dev_to_node(&pdev->dev);
+
+		/* allocate node if necessary */
+		if (!uncore->nodes[node_id])
+			uncore->nodes[node_id] = alloc_node(uncore, node_id, counters);
+
+		node = uncore->nodes[node_id];
+		if (!node) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		unit = kzalloc(sizeof(*unit), GFP_KERNEL);
+		if (!unit) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		unit->pdev = pdev;
+		unit->map = ioremap(pci_resource_start(pdev, 0),
+				    pci_resource_len(pdev, 0));
+		list_add(&unit->entry, &node->unit_list);
+		node->nr_units++;
+		found++;
+	}
+
+	if (!found)
+		return -ENODEV;
+
+	cpuhp_state_add_instance_nocalls(CPUHP_AP_UNCORE_CAVIUM_ONLINE,
+                                         &uncore->node);
+
+	/*
+	 * perf PMU is CPU dependent in difference to our uncore devices.
+	 * Just pick a CPU and migrate away if it goes offline.
+	 */
+	cpumask_set_cpu(smp_processor_id(), &uncore->active_mask);
+
+	uncore->pmu = *pmu;
+	ret = perf_pmu_register(&uncore->pmu, uncore->pmu.name, -1);
+	if (ret)
+		goto fail;
+
+	return 0;
+
Quoted 8 lines

drivers/perf/uncore/uncore_cavium.c line 321

Quoted 6 lines
drivers/perf/uncore/uncore_cavium.c new file +6 -0
+fail:
+	node_id = 0;
+	while (uncore->nodes[node_id]) {
+		node = uncore->nodes[node_id];
+
+		list_for_each_entry_safe(unit, tmp, &node->unit_list, entry) {

Why do you need the _safe variant?

OK, not needed

Quoted 1 line

Will

#4 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Quoted 12 lines · Jan Glauber
Quoted 10 lines · Will Deacon

drivers/perf/uncore/uncore_cavium.c line 213

Quoted 6 lines · Jan Glauber
drivers/perf/uncore/uncore_cavium.c new file +6 -0
+/* node attribute depending on number of NUMA nodes */
+static ssize_t node_show(struct device *dev, struct device_attribute *attr,
+			 char *page)
+{
+	if (NODES_SHIFT)
+		return sprintf(page, "config:16-%d\n", 16 + NODES_SHIFT - 1);

If NODES_SHIFT is 1, you'll end up with "config:16-16", which might confuse userspace.

Quoted 2 lines

So should I use "config:16" in that case? Is it OK to use this also for NODES_SHIFT=0 ?

If you only need one bit, then "config:16" is the right thing to do.

Will

maintainer
#5 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

In reply to Jan Glauber

Hi Jan,

Apologies for the delay in getting to this.

Quoted 6 lines · Jan Glauber
drivers/perf/uncore/uncore_cavium.c new file +0 -0
new file mode 100644
index 0000000..a7b4277

drivers/perf/uncore/uncore_cavium.c line 10

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
@@ -0,0 +1,351 @@
+/*
+ * Cavium Thunder uncore PMU support.
+ *
+ * Copyright (C) 2015,2016 Cavium Inc.
+ * Author: Jan Glauber <[email protected]>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/numa.h>
+#include <linux/slab.h>

I believe the following includes are necessary for APIs and/or data explicitly referenced by the driver code:

#include <linux/atomic.h>
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/perf_event.h>
#include <linux/printk.h>
#include <linux/smp.h>
#include <linux/sysfs.h>
#include <linux/types.h>

#include <asm/local64.h>

... please add those here.

[...]

drivers/perf/uncore/uncore_cavium.c line 23

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+/*
+ * Some notes about the various counters supported by this "uncore" PMU
+ * and the design:
+ *
+ * All counters are 64 bit long.
+ * There are no overflow interrupts.
+ * Counters are summarized per node/socket.
+ * Most devices appear as separate PCI devices per socket with the exception
+ * of OCX TLK which appears as one PCI device per socket and contains several
+ * units with counters that are merged.

As a general note, as I commented on the QC L2 PMU driver [1,2], we need to figure out if we should be aggregating physical PMUs or not.

Judging by subsequent patches, each unit has individual counters and controls, and thus we cannot atomically read/write counters or controls across them. As such, I do not think we should aggregate them, and should expose them separately to userspace.

That will simplify a number of things (e.g. the CPU migration code no longer has to iterate over a list of units).

Quoted 34 lines
drivers/perf/uncore/uncore_cavium.c new file +34 -0
+ * Some counters are selected via a control register (L2C TAD) and read by
+ * a number of counter registers, others (L2C CBC, LMC & OCX TLK) have
+ * one dedicated counter per event.
+ * Some counters are not stoppable (L2C CBC & LMC).
+ * Some counters are read-only (LMC).
+ * All counters belong to PCI devices, the devices may have additional
+ * drivers but we assume we are the only user of the counter registers.
+ * We map the whole PCI BAR so we must be careful to forbid access to
+ * addresses that contain neither counters nor counter control registers.
+ */
+
+void thunder_uncore_read(struct perf_event *event)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 prev, delta, new = 0;
+
+	node = get_node(hwc->config, uncore);
+
+	/* read counter values from all units on the node */
+	list_for_each_entry(unit, &node->unit_list, entry)
+		new += readq(hwc->event_base + unit->map);
+
+	prev = local64_read(&hwc->prev_count);
+	local64_set(&hwc->prev_count, new);
+	delta = new - prev;
+	local64_add(delta, &event->count);
+}
+
+int thunder_uncore_add(struct perf_event *event, int flags, u64 config_base,
+		       u64 event_base)
+{

drivers/perf/uncore/uncore_cavium.c line 67

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int id;
+
+	node = get_node(hwc->config, uncore);
+	id = get_id(hwc->config);
+
+	if (!cmpxchg(&node->events[id], NULL, event))
+		hwc->idx = id;

Judging by thunder_uncore_event_init() and get_id(), the specific counter to use is chosen by the user, rather than allocated as necessary. Yet the block comment before thunder_uncore_read() said only some events have a dedicated counter.

This does not seem right. Why are we not choosing a relevant counter dynamically?

As Will commented, we shouldn't need a full-barrier cmpxchg() here; the pmu::{add,del} are serialised by the core perf code as ctx->lock has to be held (and we have no interrupt to worry about). If we want to use cmpxchg() for convenience, it can be a cmpxchg_relaxed().

Quoted 21 lines
drivers/perf/uncore/uncore_cavium.c new file +21 -0
+	if (hwc->idx == -1)
+		return -EBUSY;
+
+	hwc->config_base = config_base;
+	hwc->event_base = event_base;
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (flags & PERF_EF_START)
+		uncore->pmu.start(event, PERF_EF_RELOAD);
+
+	return 0;
+}
+
+void thunder_uncore_del(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = to_uncore(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int i;
+
+	event->pmu->stop(event, PERF_EF_UPDATE);

drivers/perf/uncore/uncore_cavium.c line 99

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+
+	/*
+	 * For programmable counters we need to check where we installed it.
+	 * To keep this function generic always test the more complicated
+	 * case (free running counters won't need the loop).
+	 */
+	node = get_node(hwc->config, uncore);
+	for (i = 0; i < node->num_counters; i++) {
+		if (cmpxchg(&node->events[i], event, NULL) == event)
+			break;

Likewise, this can be cmpxchg_relaxed().

[...]

drivers/perf/uncore/uncore_cavium.c line 158

Quoted 6 lines
drivers/perf/uncore/uncore_cavium.c new file +5 -0
+int thunder_uncore_event_init(struct perf_event *event)
+{
 
+	uncore = to_uncore(event->pmu);
+	if (!uncore)
+		return -ENODEV;

Given to_uncore() uses container_of(), we can lose the check here; the result cannot be NULL.

drivers/perf/uncore/uncore_cavium.c

Quoted 2 lines
drivers/perf/uncore/uncore_cavium.c new file +2 -0
+	if (!uncore->event_valid(event->attr.config & UNCORE_EVENT_ID_MASK))
+		return -EINVAL;

Judging by the header, it looks like the node gets dropped in the high bits. I'm not sure it makes sense to encode that in the user ABI given the aggregation comments above.

In x86 uncore PMU drivers, one cpu per node is exposed in the cpumask, and that's how they target nodes. We should either do that, or have completely separate instances.

Either way, that will also remove the need for exposing the varying NODE_SHIFT under sysfs.

drivers/perf/uncore/uncore_cavium.c line 167

Quoted 7 lines
drivers/perf/uncore/uncore_cavium.c new file +7 -0
+
+	/* check NUMA node */
+	node = get_node(event->attr.config, uncore);
+	if (!node) {
+		pr_debug("Invalid NUMA node selected\n");
+		return -EINVAL;
+	}

... and this to, since the node will either be implicit in the cpu performing the monitoring, or in the PMU instance the event was requested from.

drivers/perf/uncore/uncore_cavium.c line 172

Quoted 5 lines
drivers/perf/uncore/uncore_cavium.c new file +5 -0
+
+	hwc->config = event->attr.config;
+	hwc->idx = -1;
+	return 0;
+}

I believe that we should also check that the leader (and siblings) are compatible.

Something like l2x0_pmu_group_is_valid in arch/arm/mm/cache-l2x0-pmu.c.

We also need to ensure that the events in a group are all on the same CPU (the one exposed via the cpumask). The l2x0 PMU also does this in its event_init path.

[...]

Quoted 15 lines
drivers/perf/uncore/uncore_cavium.c new file +13 -0
+	cpuhp_state_add_instance_nocalls(CPUHP_AP_UNCORE_CAVIUM_ONLINE,
+                                         &uncore->node);
 
+	ret = perf_pmu_register(&uncore->pmu, uncore->pmu.name, -1);
+	if (ret)
+		goto fail;
 
+fail:
+	node_id = 0;
+	while (uncore->nodes[node_id]) {
+		node = uncore->nodes[node_id];
+
+		list_for_each_entry_safe(unit, tmp, &node->unit_list, entry) {
+			if (unit->pdev) {
+				if (unit->map)

drivers/perf/uncore/uncore_cavium.c line 333

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
+					iounmap(unit->map);
+				pci_dev_put(unit->pdev);
+			}
+			kfree(unit);
+		}
+		kfree(uncore->nodes[node_id]);
+		node_id++;
+	}
+	return ret;
+}

Shouldn't we remove the instance from the cpuhp state machine in the failure path?

[...]

drivers/perf/uncore/uncore_cavium.h line 4

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.h new file +4 -0
new file mode 100644
index 0000000..b5d64b5
@@ -0,0 +1,71 @@
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/perf_event.h>

I believe this header also needs:

#include <linux/cpumask.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
#include <linux/sysfs.h>
#include <linux/types.h>

drivers/perf/uncore/uncore_cavium.h line 7

Quoted 3 lines
drivers/perf/uncore/uncore_cavium.h new file +3 -0
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "thunderx_uncore: " fmt

IIRC this needs to be set before including <linux/printk.h>. Does this work reliably, given that printk.h is likely included first?

Thanks,
Mark.

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-November/466764.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-November/466768.html

#6 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Quoted 19 lines · Mark Rutland
Quoted 5 lines · Jan Glauber
drivers/perf/uncore/uncore_cavium.c new file +0 -0
new file mode 100644
index 0000000..a7b4277

drivers/perf/uncore/uncore_cavium.c line 23

Quoted 9 lines
drivers/perf/uncore/uncore_cavium.c new file +9 -0
+ * Some notes about the various counters supported by this "uncore" PMU
+ * and the design:
+ *
+ * All counters are 64 bit long.
+ * There are no overflow interrupts.
+ * Counters are summarized per node/socket.
+ * Most devices appear as separate PCI devices per socket with the exception
+ * of OCX TLK which appears as one PCI device per socket and contains several
+ * units with counters that are merged.

As a general note, as I commented on the QC L2 PMU driver [1,2], we need to figure out if we should be aggregating physical PMUs or not.

Quoted 4 lines

Judging by subsequent patches, each unit has individual counters and controls, and thus we cannot atomically read/write counters or controls across them. As such, I do not think we should aggregate them, and should expose them separately to userspace.

I thought each unit was registered as a separate PMU to perf? Or are you specifically commenting on the OCX TLK? The comment there suggests that the units cannot be individually enabled/disabled and, without docs, I trust that's the case.

Will

#7 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

In reply to Mark Rutland
Quoted 11 lines · Mark Rutland
Quoted 10 lines

+/*
+ * Some notes about the various counters supported by this "uncore" PMU
+ * and the design:
+ *
+ * All counters are 64 bit long.
+ * There are no overflow interrupts.
+ * Counters are summarized per node/socket.
+ * Most devices appear as separate PCI devices per socket with the exception
+ * of OCX TLK which appears as one PCI device per socket and contains several
+ * units with counters that are merged.

Quoted 2 lines

As a general note, as I commented on the QC L2 PMU driver [1,2], we need to figure out if we should be aggregating physical PMUs or not.

As said before, although it would be possible to create separate PMUs for each unit, the individual counters are not interesting. For example we are not interested in individual counters of Tag-and-data unit 0..7, we just want the global view.

Quoted 4 lines

Judging by subsequent patches, each unit has individual counters and controls, and thus we cannot atomically read/write counters or controls across them. As such, I do not think we should aggregate them, and should expose them separately to userspace.

That sounds like just moving the problem of aggregating the counters to user-space. And would make the results even worse, if the user needs several calls to summarize the counters, given how slow a perf counter read is.

Quoted 2 lines

That will simplify a number of things (e.g. the CPU migration code no longer has to iterate over a list of units).

Sure, it simplifies the kernel part, but it moves the cost to the user.

#8 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

In reply to Mark Rutland

Hi Mark,

thanks for reviewing. One question below, for most of your other comments I think we need to come to a conclusion about the aggregation first.

Quoted 40 lines · Mark Rutland

Hi Jan,

Apologies for the delay in getting to this.

Quoted 6 lines · Jan Glauber
drivers/perf/uncore/uncore_cavium.c new file +0 -0
new file mode 100644
index 0000000..a7b4277

drivers/perf/uncore/uncore_cavium.c line 10

Quoted 10 lines
drivers/perf/uncore/uncore_cavium.c new file +10 -0
@@ -0,0 +1,351 @@
+/*
+ * Cavium Thunder uncore PMU support.
+ *
+ * Copyright (C) 2015,2016 Cavium Inc.
+ * Author: Jan Glauber <[email protected]>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/numa.h>
+#include <linux/slab.h>

I believe the following includes are necessary for APIs and/or data explicitly referenced by the driver code:

#include <linux/atomic.h>
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/perf_event.h>
#include <linux/printk.h>
#include <linux/smp.h>
#include <linux/sysfs.h>
#include <linux/types.h>

Quoted 3 lines

#include <asm/local64.h>

... please add those here.

Should I also add includes that are already in the included by uncore_cavium.h? I usually avoid includes that come through the "local" header file.

maintainer
#9 lore raw

Re: [PATCH v4 1/5] arm64: perf: Basic uncore counter support for Cavium ThunderX SOC

Quoted 14 lines · Jan Glauber

Hi Mark,

thanks for reviewing. One question below,

Quoted 8 lines · Mark Rutland

drivers/perf/uncore/uncore_cavium.c line 10

Quoted 3 lines · Jan Glauber
drivers/perf/uncore/uncore_cavium.c new file +3 -0
+#include <linux/cpufeature.h>
+#include <linux/numa.h>
+#include <linux/slab.h>

I believe the following includes are necessary for APIs and/or data explicitly referenced by the driver code:

[...]

Quoted 1 line

Should I also add includes that are already in the included by uncore_cavium.h?

Please do.

Quoted 1 line

I usually avoid includes that come through the "local" header file.

Generally, when you explcitly use some macro/function/data in a file, that file should have the relevant include.

If something's only used in the header (e.g. hidden in a macro or inline function), then we only need that include in the header.

For example: uncore_cavium.h uses container_of(), and should include <linux/kernel.h>. Also, uncore_cavium.c also uses container_of() directly for something unrelated, and should also include <linux/kernel.h>.

Thanks,
Mark.

#10 lore raw

PATCH v4 2/5 arm64: perf: Cavium ThunderX L2C TAD uncore support

In reply to Jan Glauber

Support counters of the L2 Cache tag and data units.

---
 drivers/perf/uncore/Makefile                |   3 +-
 drivers/perf/uncore/uncore_cavium.c         |   1 +
 drivers/perf/uncore/uncore_cavium.h         |   1 +
 drivers/perf/uncore/uncore_cavium_l2c_tad.c | 379 ++++++++++++++++++++++++++++
 4 files changed, 383 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_tad.c
drivers/perf/uncore/Makefile +2 -1
index 6130e18..90850a2 100644
@@ -1 +1,2 @@
-obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o
+obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o		\
+				   uncore_cavium_l2c_tad.o
drivers/perf/uncore/uncore_cavium.c +1 -0
index a7b4277..15e1aec 100644
@@ -346,6 +346,7 @@ static int __init thunder_uncore_init(void)
 	if (ret)
 		return ret;
 
+	thunder_uncore_l2c_tad_setup();
 	return 0;
 }
 late_initcall(thunder_uncore_init);
drivers/perf/uncore/uncore_cavium.h +1 -0
index b5d64b5..70a8214 100644
@@ -69,3 +69,4 @@ int thunder_uncore_setup(struct thunder_uncore *uncore, int id,
 ssize_t thunder_events_sysfs_show(struct device *dev,
 				  struct device_attribute *attr,
 				  char *page);
+int thunder_uncore_l2c_tad_setup(void);
drivers/perf/uncore/uncore_cavium_l2c_tad.c new file +379 -0

379 lines not on this page · raw message

Signature · 1 line
2.9.0.rc0.21.g7777322
#11 lore raw

PATCH v4 3/5 arm64: perf: Cavium ThunderX L2C CBC uncore support

In reply to Jan Glauber

Support counters of the L2 cache crossbar connect.

---
 drivers/perf/uncore/Makefile                |   3 +-
 drivers/perf/uncore/uncore_cavium.c         |   1 +
 drivers/perf/uncore/uncore_cavium.h         |   1 +
 drivers/perf/uncore/uncore_cavium_l2c_cbc.c | 148 ++++++++++++++++++++++++++++
 4 files changed, 152 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_cbc.c
drivers/perf/uncore/Makefile +2 -1
index 90850a2..d5ef3db 100644
@@ -1,2 +1,3 @@
 obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o		\
-				   uncore_cavium_l2c_tad.o
+				   uncore_cavium_l2c_tad.o	\
+				   uncore_cavium_l2c_cbc.o
drivers/perf/uncore/uncore_cavium.c +1 -0
index 15e1aec..051f0fa 100644
@@ -347,6 +347,7 @@ static int __init thunder_uncore_init(void)
 		return ret;
 
 	thunder_uncore_l2c_tad_setup();
+	thunder_uncore_l2c_cbc_setup();
 	return 0;
 }
 late_initcall(thunder_uncore_init);
drivers/perf/uncore/uncore_cavium.h +1 -0
index 70a8214..91d674a 100644
@@ -70,3 +70,4 @@ ssize_t thunder_events_sysfs_show(struct device *dev,
 				  struct device_attribute *attr,
 				  char *page);
 int thunder_uncore_l2c_tad_setup(void);
+int thunder_uncore_l2c_cbc_setup(void);
drivers/perf/uncore/uncore_cavium_l2c_cbc.c new file +148 -0

148 lines not on this page · raw message

Signature · 1 line
2.9.0.rc0.21.g7777322
#12 lore raw

PATCH v4 4/5 arm64: perf: Cavium ThunderX LMC uncore support

In reply to Jan Glauber

Support counters on the DRAM controllers.

---
 drivers/perf/uncore/Makefile            |   3 +-
 drivers/perf/uncore/uncore_cavium.c     |   1 +
 drivers/perf/uncore/uncore_cavium.h     |   1 +
 drivers/perf/uncore/uncore_cavium_lmc.c | 118 ++++++++++++++++++++++++++++++++
 4 files changed, 122 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_lmc.c
drivers/perf/uncore/Makefile +2 -1
index d5ef3db..ef04a2b9 100644
@@ -1,3 +1,4 @@
 obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o		\
 				   uncore_cavium_l2c_tad.o	\
-				   uncore_cavium_l2c_cbc.o
+				   uncore_cavium_l2c_cbc.o	\
+				   uncore_cavium_lmc.o
drivers/perf/uncore/uncore_cavium.c +1 -0
index 051f0fa..fd9e49e 100644
@@ -348,6 +348,7 @@ static int __init thunder_uncore_init(void)
 
 	thunder_uncore_l2c_tad_setup();
 	thunder_uncore_l2c_cbc_setup();
+	thunder_uncore_lmc_setup();
 	return 0;
 }
 late_initcall(thunder_uncore_init);
drivers/perf/uncore/uncore_cavium.h +1 -0
index 91d674a..3897586 100644
@@ -71,3 +71,4 @@ ssize_t thunder_events_sysfs_show(struct device *dev,
 				  char *page);
 int thunder_uncore_l2c_tad_setup(void);
 int thunder_uncore_l2c_cbc_setup(void);
+int thunder_uncore_lmc_setup(void);
drivers/perf/uncore/uncore_cavium_lmc.c new file +118 -0

118 lines not on this page · raw message

Signature · 1 line
2.9.0.rc0.21.g7777322
#13 lore raw

PATCH v4 5/5 arm64: perf: Cavium ThunderX OCX TLK uncore support

In reply to Jan Glauber

Support for the OCX transmit link counters.

---
 drivers/perf/uncore/Makefile                |   3 +-
 drivers/perf/uncore/uncore_cavium.c         |   1 +
 drivers/perf/uncore/uncore_cavium.h         |   1 +
 drivers/perf/uncore/uncore_cavium_ocx_tlk.c | 344 ++++++++++++++++++++++++++++
 4 files changed, 348 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_ocx_tlk.c
drivers/perf/uncore/Makefile +2 -1
index ef04a2b9..7e2e8e5 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_UNCORE_PMU_CAVIUM) += uncore_cavium.o		\
 				   uncore_cavium_l2c_tad.o	\
 				   uncore_cavium_l2c_cbc.o	\
-				   uncore_cavium_lmc.o
+				   uncore_cavium_lmc.o		\
+				   uncore_cavium_ocx_tlk.o
drivers/perf/uncore/uncore_cavium.c +1 -0
index fd9e49e..46ced45 100644
@@ -349,6 +349,7 @@ static int __init thunder_uncore_init(void)
 	thunder_uncore_l2c_tad_setup();
 	thunder_uncore_l2c_cbc_setup();
 	thunder_uncore_lmc_setup();
+	thunder_uncore_ocx_tlk_setup();
 	return 0;
 }
 late_initcall(thunder_uncore_init);
drivers/perf/uncore/uncore_cavium.h +1 -0
index 3897586..43ab426 100644
@@ -72,3 +72,4 @@ ssize_t thunder_events_sysfs_show(struct device *dev,
 int thunder_uncore_l2c_tad_setup(void);
 int thunder_uncore_l2c_cbc_setup(void);
 int thunder_uncore_lmc_setup(void);
+int thunder_uncore_ocx_tlk_setup(void);
drivers/perf/uncore/uncore_cavium_ocx_tlk.c new file +344 -0

344 lines not on this page · raw message

Signature · 1 line
2.9.0.rc0.21.g7777322