// SPDX-License-Identifier: GPL-2.0-only /* * ARM DynamIQ Shared Unit (DSU) PMU driver * * Copyright (C) ARM Limited, 2017. * * Based on ARM CCI-PMU, ARMv8 PMU-v3 drivers.
*/
/* * We use the index of the counters as they appear in the counter * bit maps in the PMU registers (e.g CLUSTERPMSELR). * i.e, * counter 0 - Bit 0 * counter 1 - Bit 1 * ... * Cycle counter - Bit 31
*/ #define DSU_PMU_IDX_CYCLE_COUNTER 31
/* All event counters are 32bit, with a 64bit Cycle counter */ #define DSU_PMU_COUNTER_WIDTH(idx) \
(((idx) == DSU_PMU_IDX_CYCLE_COUNTER) ? 64 : 32)
/* * struct dsu_pmu - DSU PMU descriptor * * @pmu_lock : Protects accesses to DSU PMU register from normal vs * interrupt handler contexts. * @hw_events : Holds the event counter state. * @associated_cpus : CPUs attached to the DSU. * @active_cpu : CPU to which the PMU is bound for accesses. * @cpuhp_node : Node for CPU hotplug notifier link. * @num_counters : Number of event counters implemented by the PMU, * excluding the cycle counter. * @irq : Interrupt line for counter overflow. * @cpmceid_bitmap : Bitmap for the availability of architected common * events (event_code < 0x40).
*/ struct dsu_pmu { struct pmu pmu; struct device *dev;
raw_spinlock_t pmu_lock; struct dsu_hw_events hw_events;
cpumask_t associated_cpus;
cpumask_t active_cpu; struct hlist_node cpuhp_node;
s8 num_counters; int irq;
DECLARE_BITMAP(cpmceid_bitmap, DSU_PMU_MAX_COMMON_EVENTS);
};
raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); if (idx == DSU_PMU_IDX_CYCLE_COUNTER)
val = __dsu_pmu_read_pmccntr(); else
val = __dsu_pmu_read_counter(idx);
raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags);
do { /* We may also be called from the irq handler */
prev_count = local64_read(&hwc->prev_count);
new_count = dsu_pmu_read_counter(event);
} while (local64_cmpxchg(&hwc->prev_count, prev_count, new_count) !=
prev_count);
delta = (new_count - prev_count) & DSU_PMU_COUNTER_MASK(hwc->idx);
local64_add(delta, &event->count);
}
/* * dsu_pmu_set_event_period: Set the period for the counter. * * All DSU PMU event counters, except the cycle counter are 32bit * counters. To handle cases of extreme interrupt latency, we program * the counter with half of the max count for the counters.
*/ staticvoid dsu_pmu_set_event_period(struct perf_event *event)
{ int idx = event->hw.idx;
u64 val = DSU_PMU_COUNTER_MASK(idx) >> 1;
/* * Make sure the group of events can be scheduled at once * on the PMU.
*/ staticbool dsu_pmu_validate_group(struct perf_event *event)
{ struct perf_event *sibling, *leader = event->group_leader; struct dsu_hw_events fake_hw;
if (event->attr.type != event->pmu->type) return -ENOENT;
/* We don't support sampling */ if (is_sampling_event(event)) {
dev_dbg(dsu_pmu->pmu.dev, "Can't support sampling events\n"); return -EOPNOTSUPP;
}
/* We cannot support task bound events */ if (event->cpu < 0 || event->attach_state & PERF_ATTACH_TASK) {
dev_dbg(dsu_pmu->pmu.dev, "Can't support per-task counters\n"); return -EINVAL;
}
if (has_branch_stack(event)) {
dev_dbg(dsu_pmu->pmu.dev, "Can't support filtering\n"); return -EINVAL;
}
if (!cpumask_test_cpu(event->cpu, &dsu_pmu->associated_cpus)) {
dev_dbg(dsu_pmu->pmu.dev, "Requested cpu is not associated with the DSU\n"); return -EINVAL;
} /* * Choose the current active CPU to read the events. We don't want * to migrate the event contexts, irq handling etc to the requested * CPU. As long as the requested CPU is within the same DSU, we * are fine.
*/
event->cpu = cpumask_first(&dsu_pmu->active_cpu); if (event->cpu >= nr_cpu_ids) return -EINVAL; if (!dsu_pmu_validate_group(event)) return -EINVAL;
dsu_pmu = devm_kzalloc(&pdev->dev, sizeof(*dsu_pmu), GFP_KERNEL); if (!dsu_pmu) return ERR_PTR(-ENOMEM);
raw_spin_lock_init(&dsu_pmu->pmu_lock); /* * Initialise the number of counters to -1, until we probe * the real number on a connected CPU.
*/
dsu_pmu->num_counters = -1; return dsu_pmu;
}
/* * dsu_pmu_dt_get_cpus: Get the list of CPUs in the cluster * from device tree.
*/ staticint dsu_pmu_dt_get_cpus(struct device *dev, cpumask_t *mask)
{ int i = 0, n, cpu; struct device_node *cpu_node;
n = of_count_phandle_with_args(dev->of_node, "cpus", NULL); if (n <= 0) return -ENODEV; for (; i < n; i++) {
cpu_node = of_parse_phandle(dev->of_node, "cpus", i); if (!cpu_node) break;
cpu = of_cpu_node_to_id(cpu_node);
of_node_put(cpu_node); /* * We have to ignore the failures here and continue scanning * the list to handle cases where the nr_cpus could be capped * in the running kernel.
*/ if (cpu < 0) continue;
cpumask_set_cpu(cpu, mask);
} return 0;
}
/* * dsu_pmu_acpi_get_cpus: Get the list of CPUs in the cluster * from ACPI.
*/ staticint dsu_pmu_acpi_get_cpus(struct device *dev, cpumask_t *mask)
{ #ifdef CONFIG_ACPI struct acpi_device *parent_adev = acpi_dev_parent(ACPI_COMPANION(dev)); int cpu;
/* * A dsu pmu node is inside a cluster parent node along with cpu nodes. * We need to find out all cpus that have the same parent with this pmu.
*/
for_each_possible_cpu(cpu) { struct acpi_device *acpi_dev; struct device *cpu_dev = get_cpu_device(cpu);
/* * dsu_pmu_probe_pmu: Probe the PMU details on a CPU in the cluster.
*/ staticvoid dsu_pmu_probe_pmu(struct dsu_pmu *dsu_pmu)
{
u64 num_counters;
u32 cpmceid[2];
num_counters = (__dsu_pmu_read_pmcr() >> CLUSTERPMCR_N_SHIFT) &
CLUSTERPMCR_N_MASK; /* We can only support up to 31 independent counters */ if (WARN_ON(num_counters > 31))
num_counters = 31;
dsu_pmu->num_counters = num_counters; if (!dsu_pmu->num_counters) return;
cpmceid[0] = __dsu_pmu_read_pmceid(0);
cpmceid[1] = __dsu_pmu_read_pmceid(1);
bitmap_from_arr32(dsu_pmu->cpmceid_bitmap, cpmceid,
DSU_PMU_MAX_COMMON_EVENTS);
}
staticvoid dsu_pmu_set_active_cpu(int cpu, struct dsu_pmu *dsu_pmu)
{
cpumask_set_cpu(cpu, &dsu_pmu->active_cpu); if (irq_set_affinity(dsu_pmu->irq, &dsu_pmu->active_cpu))
pr_warn("Failed to set irq affinity to %d\n", cpu);
}
/* * dsu_pmu_init_pmu: Initialise the DSU PMU configurations if * we haven't done it already.
*/ staticvoid dsu_pmu_init_pmu(struct dsu_pmu *dsu_pmu)
{ if (dsu_pmu->num_counters == -1)
dsu_pmu_probe_pmu(dsu_pmu); /* Reset the interrupt overflow mask */
dsu_pmu_get_reset_overflow();
}
if (!cpumask_test_and_clear_cpu(cpu, &dsu_pmu->active_cpu)) return 0;
dst = cpumask_any_and_but(&dsu_pmu->associated_cpus,
cpu_online_mask, cpu); /* If there are no active CPUs in the DSU, leave IRQ disabled */ if (dst >= nr_cpu_ids) return 0;
MODULE_DESCRIPTION("Perf driver for ARM DynamIQ Shared Unit");
MODULE_AUTHOR("Suzuki K Poulose ");
MODULE_LICENSE("GPL v2");
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.12Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.