// SPDX-License-Identifier: GPL-2.0+ // // Linux performance counter support for ARC CPUs. // This code is inspired by the perf support of various other architectures. // // Copyright (C) 2013-2018 Synopsys, Inc. (www.synopsys.com)
/* HW holds 8 symbols + one for null terminator */ #define ARCPMU_EVENT_NAME_LEN 9
/* * Some ARC pct quirks: * * PERF_COUNT_HW_STALLED_CYCLES_BACKEND * PERF_COUNT_HW_STALLED_CYCLES_FRONTEND * The ARC 700 can either measure stalls per pipeline stage, or all stalls * combined; for now we assign all stalls to STALLED_CYCLES_BACKEND * and all pipeline flushes (e.g. caused by mispredicts, etc.) to * STALLED_CYCLES_FRONTEND. * * We could start multiple performance counters and combine everything * afterwards, but that makes it complicated. * * Note that I$ cache misses aren't counted by either of the two!
*/
/* * ARC PCT has hardware conditions with fixed "names" but variable "indexes" * (based on a specific RTL build) * Below is the static map between perf generic/arc specific event_id and * h/w condition names. * At the time of probe, we loop thru each index and find its name to * complete the mapping of perf event_id to h/w index as latter is needed * to program the counter really
*/ staticconstchar * const arc_pmu_ev_hw_map[] = { /* count cycles */
[PERF_COUNT_HW_CPU_CYCLES] = "crun",
[PERF_COUNT_HW_REF_CPU_CYCLES] = "crun",
[PERF_COUNT_HW_BUS_CYCLES] = "crun",
struct arc_pmu_cpu { /* * A 1 bit for an index indicates that the counter is being used for * an event. A 0 means that the counter can be used.
*/ unsignedlong used_mask[BITS_TO_LONGS(ARC_PERF_MAX_COUNTERS)];
/* * The events that are active on the PMU for the given index.
*/ struct perf_event *act_counter[ARC_PERF_MAX_COUNTERS];
};
struct arc_callchain_trace { int depth; void *perf_stuff;
};
void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
{ /* * User stack can't be unwound trivially with kernel dwarf unwinder * So for now just record the user PC
*/
perf_callchain_store(entry, instruction_pointer(regs));
}
/* * ARC supports making 'snapshots' of the counters, so we don't * need to care about counters wrapping to 0 underneath our feet
*/
write_aux_reg(ARC_REG_PCT_INDEX, idx);
tmp = read_aux_reg(ARC_REG_PCT_CONTROL);
write_aux_reg(ARC_REG_PCT_CONTROL, tmp | ARC_REG_PCT_CONTROL_SN);
result = (u64) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32;
result |= read_aux_reg(ARC_REG_PCT_SNAPL);
/* * We aren't afraid of hwc->prev_count changing beneath our feet * because there's no way for us to re-enter this function anytime.
*/
local64_set(&hwc->prev_count, new_raw_count);
local64_add(delta, &event->count);
local64_sub(delta, &hwc->period_left);
}
staticint arc_pmu_event_set_period(struct perf_event *event)
{ struct hw_perf_event *hwc = &event->hw;
s64 left = local64_read(&hwc->period_left);
s64 period = hwc->sample_period; int idx = hwc->idx; int overflow = 0;
u64 value;
if (unlikely(left <= -period)) { /* left underflowed by more than period. */
left = period;
local64_set(&hwc->period_left, left);
hwc->last_period = period;
overflow = 1;
} elseif (unlikely(left <= 0)) { /* left underflowed by less than period. */
left += period;
local64_set(&hwc->period_left, left);
hwc->last_period = period;
overflow = 1;
}
if (left > arc_pmu->max_period)
left = arc_pmu->max_period;
value = arc_pmu->max_period - left;
local64_set(&hwc->prev_count, value);
/* Write value */
write_aux_reg(ARC_REG_PCT_COUNTL, lower_32_bits(value));
write_aux_reg(ARC_REG_PCT_COUNTH, upper_32_bits(value));
perf_event_update_userpage(event);
return overflow;
}
/* * Assigns hardware counter to hardware condition. * Note that there is no separate start/stop mechanism; * stopping is achieved by assigning the 'never' condition
*/ staticvoid arc_pmu_start(struct perf_event *event, int flags)
{ struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx;
if (WARN_ON_ONCE(idx == -1)) return;
if (flags & PERF_EF_RELOAD)
WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
hwc->state = 0;
arc_pmu_event_set_period(event);
/* Enable interrupt for this counter */ if (is_sampling_event(event))
write_aux_reg(ARC_REG_PCT_INT_CTRL,
read_aux_reg(ARC_REG_PCT_INT_CTRL) | BIT(idx));
staticvoid arc_pmu_stop(struct perf_event *event, int flags)
{ struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx;
/* Disable interrupt for this counter */ if (is_sampling_event(event)) { /* * Reset interrupt flag by writing of 1. This is required * to make sure pending interrupt was not left.
*/
write_aux_reg(ARC_REG_PCT_INT_ACT, BIT(idx));
write_aux_reg(ARC_REG_PCT_INT_CTRL,
read_aux_reg(ARC_REG_PCT_INT_CTRL) & ~BIT(idx));
}
if (!(event->hw.state & PERF_HES_STOPPED)) { /* stop hw counter here */
write_aux_reg(ARC_REG_PCT_INDEX, idx);
/* condition code #0 is always "never" */
write_aux_reg(ARC_REG_PCT_CONFIG, 0);
if (is_sampling_event(event)) { /* Mimic full counter overflow as other arches do */
write_aux_reg(ARC_REG_PCT_INT_CNTL,
lower_32_bits(arc_pmu->max_period));
write_aux_reg(ARC_REG_PCT_INT_CNTH,
upper_32_bits(arc_pmu->max_period));
}
active_ints = read_aux_reg(ARC_REG_PCT_INT_ACT); if (!active_ints) goto done;
regs = get_irq_regs();
do { struct perf_event *event; struct hw_perf_event *hwc;
idx = __ffs(active_ints);
/* Reset interrupt flag by writing of 1 */
write_aux_reg(ARC_REG_PCT_INT_ACT, BIT(idx));
/* * On reset of "interrupt active" bit corresponding * "interrupt enable" bit gets automatically reset as well. * Now we need to re-enable interrupt for the counter.
*/
write_aux_reg(ARC_REG_PCT_INT_CTRL,
read_aux_reg(ARC_REG_PCT_INT_CTRL) | BIT(idx));
/* * We don't add attrs here as we don't have pre-defined list of perf events. * We will generate and add attrs dynamically in probe() after we read HW * configuration.
*/ staticstruct attribute_group arc_pmu_events_attr_gr = {
.name = "events",
};
staticinlinebool event_in_hw_event_map(int i, char *name)
{ if (!arc_pmu_ev_hw_map[i]) returnfalse;
if (!strlen(arc_pmu_ev_hw_map[i])) returnfalse;
if (strcmp(arc_pmu_ev_hw_map[i], name)) returnfalse;
returntrue;
}
staticvoid arc_pmu_map_hw_event(int j, char *str)
{ int i;
/* See if HW condition has been mapped to a perf event_id */ for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) { if (event_in_hw_event_map(i, str)) {
pr_debug("mapping perf event %2d to h/w event \'%8s\' (idx %d)\n",
i, str, j);
arc_pmu->ev_hw_idx[i] = j;
}
}
}
staticint arc_pmu_device_probe(struct platform_device *pdev)
{ struct arc_reg_pct_build pct_bcr; struct arc_reg_cc_build cc_bcr; int i, has_interrupts, irq = -1; int counter_size; /* in bits */
READ_BCR(ARC_REG_PCT_BUILD, pct_bcr); if (!pct_bcr.v) {
pr_err("This core does not have performance counters!\n"); return -ENODEV;
}
BUILD_BUG_ON(ARC_PERF_MAX_COUNTERS > 32); if (WARN_ON(pct_bcr.c > ARC_PERF_MAX_COUNTERS)) return -EINVAL;
READ_BCR(ARC_REG_CC_BUILD, cc_bcr); if (WARN(!cc_bcr.v, "Counters exist but No countable conditions?")) return -EINVAL;
arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu), GFP_KERNEL); if (!arc_pmu) return -ENOMEM;
arc_pmu->n_events = cc_bcr.c;
if (arc_pmu_raw_alloc(&pdev->dev)) return -ENOMEM;
if (has_interrupts) {
irq = platform_get_irq(pdev, 0); if (irq >= 0) { int ret;
arc_pmu->irq = irq;
/* intc map function ensures irq_set_percpu_devid() called */
ret = request_percpu_irq(irq, arc_pmu_intr, "ARC perf counters",
this_cpu_ptr(&arc_pmu_cpu));
if (!ret)
on_each_cpu(arc_cpu_pmu_irq_init, &irq, 1); else
irq = -1;
}
}
if (irq == -1)
arc_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
/* * perf parser doesn't really like '-' symbol in events name, so let's * use '_' in arc pct name as it goes to kernel PMU event prefix.
*/ return perf_pmu_register(&arc_pmu->pmu, "arc_pct", PERF_TYPE_RAW);
}
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.