/* * The cache is made up of one or more clusters, each cluster has its own PMU. * Each cluster is associated with one or more CPUs. * This structure represents one of the hardware PMUs. * * Events can be envisioned as a 2-dimensional array. Each column represents * a group of events. There are 8 groups. Only one entry from each * group can be in use at a time. * * Events are specified as 0xCCG, where CC is 2 hex digits specifying * the code (array row) and G specifies the group (column). * * In addition there is a cycle counter event specified by L2CYCLE_CTR_RAW_CODE * which is outside the above scheme.
*/ struct cluster_pmu { struct list_head next; struct perf_event *events[MAX_L2_CTRS]; struct l2cache_pmu *l2cache_pmu;
DECLARE_BITMAP(used_counters, MAX_L2_CTRS);
DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1); int irq; int cluster_id; /* The CPU that is used for collecting events on this cluster */ int on_cpu; /* All the CPUs associated with this cluster */
cpumask_t cluster_cpus;
spinlock_t pmu_lock;
};
/* * Hardware allows filtering of events based on the originating * CPU. Turn this off by setting filter bits to allow events from * all CPUS, subunits and ID independent events in this cluster.
*/ staticinlinevoid cluster_pmu_set_evfilter_sys_mode(u32 ctr)
{
u32 val = L2PMXEVFILTER_SUFILTER_ALL |
L2PMXEVFILTER_ORGFILTER_IDINDEP |
L2PMXEVFILTER_ORGFILTER_ALL;
do {
prev = local64_read(&hwc->prev_count);
now = cluster_pmu_counter_get_value(idx);
} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
/* * The cycle counter is 64-bit, but all other counters are * 32-bit, and we must handle 32-bit overflow explicitly.
*/
delta = now - prev; if (idx != l2_cycle_ctr_idx)
delta &= 0xffffffff;
/* * We limit the max period to half the max counter value so * that even in the case of extreme interrupt latency the * counter will (hopefully) not wrap past its initial value.
*/ if (idx == l2_cycle_ctr_idx) new = L2_CYCLE_COUNTER_RELOAD; else new = L2_COUNTER_RELOAD;
if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) { if (test_and_set_bit(l2_cycle_ctr_idx, cluster->used_counters)) return -EAGAIN;
return l2_cycle_ctr_idx;
}
idx = find_first_zero_bit(cluster->used_counters, num_ctrs); if (idx == num_ctrs) /* The counters are all in use. */ return -EAGAIN;
/* * Check for column exclusion: event column already in use by another * event. This is for events which are not in the same group. * Conflicting events in the same group are detected in event_init.
*/
group = L2_EVT_GROUP(hwc->config_base); if (test_bit(group, cluster->used_groups)) return -EAGAIN;
if (!cluster_pmu_counter_has_overflowed(ovsr, idx)) continue;
l2_cache_event_update(event);
hwc = &event->hw;
l2_cache_cluster_set_period(cluster, hwc);
}
return IRQ_HANDLED;
}
/* * Implementation of abstract pmu functionality required by * the core perf events code.
*/
staticvoid l2_cache_pmu_enable(struct pmu *pmu)
{ /* * Although there is only one PMU (per socket) controlling multiple * physical PMUs (per cluster), because we do not support per-task mode * each event is associated with a CPU. Each event has pmu_enable * called on its CPU, so here it is only necessary to enable the * counters for the current CPU.
*/
cluster = get_cluster_pmu(l2cache_pmu, event->cpu); if (!cluster) { /* CPU has not been initialised */
dev_dbg_ratelimited(&l2cache_pmu->pdev->dev, "CPU%d not associated with L2 cluster\n", event->cpu); return -EINVAL;
}
/* Ensure all events in a group are on the same cpu */ if ((event->group_leader != event) &&
(cluster->on_cpu != event->group_leader->cpu)) {
dev_dbg_ratelimited(&l2cache_pmu->pdev->dev, "Can't create group on CPUs %d and %d",
event->cpu, event->group_leader->cpu); return -EINVAL;
}
/* * Ensure all events are on the same cpu so all events are in the * same cpu context, to avoid races on pmu_enable etc.
*/
event->cpu = cluster->on_cpu;
/* CCG format for perf RAW codes. */
PMU_FORMAT_ATTR(l2_code, "config:4-11");
PMU_FORMAT_ATTR(l2_group, "config:0-3");
PMU_FORMAT_ATTR(event, "config:0-11");
/* * Read number of counters from L2PMCR and add 1 * for the cycle counter.
*/ return ((val >> L2PMCR_NUM_EV_SHIFT) & L2PMCR_NUM_EV_MASK) + 1;
}
staticstruct cluster_pmu *l2_cache_associate_cpu_with_cluster( struct l2cache_pmu *l2cache_pmu, int cpu)
{
u64 mpidr; int cpu_cluster_id; struct cluster_pmu *cluster;
/* * This assumes that the cluster_id is in MPIDR[aff1] for * single-threaded cores, and MPIDR[aff2] for multi-threaded * cores. This logic will have to be updated if this changes.
*/
mpidr = read_cpuid_mpidr(); if (mpidr & MPIDR_MT_BITMASK)
cpu_cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 2); else
cpu_cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
list_for_each_entry(cluster, &l2cache_pmu->clusters, next) { if (cluster->cluster_id != cpu_cluster_id) continue;
l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
cluster = get_cluster_pmu(l2cache_pmu, cpu); if (!cluster) { /* First time this CPU has come online */
cluster = l2_cache_associate_cpu_with_cluster(l2cache_pmu, cpu); if (!cluster) { /* Only if broken firmware doesn't list every cluster */
WARN_ONCE(1, "No L2 cache cluster for CPU%d\n", cpu); return 0;
}
}
/* If another CPU is managing this cluster, we're done */ if (cluster->on_cpu != -1) return 0;
/* * All CPUs on this cluster were down, use this one. * Reset to put it into sane state.
*/
cluster->on_cpu = cpu;
cpumask_set_cpu(cpu, &l2cache_pmu->cpumask);
cluster_pmu_reset();
/* If this CPU is not managing the cluster, we're done */ if (cluster->on_cpu != cpu) return 0;
/* Give up ownership of cluster */
cpumask_clear_cpu(cpu, &l2cache_pmu->cpumask);
cluster->on_cpu = -1;
/* Any other CPU for this cluster which is still online */
target = cpumask_any_and_but(&cluster->cluster_cpus,
cpu_online_mask, cpu); if (target >= nr_cpu_ids) {
disable_irq(cluster->irq); return 0;
}
/* Read cluster info and initialize each cluster */
err = device_for_each_child(&pdev->dev, l2cache_pmu,
l2_cache_pmu_probe_cluster); if (err) return err;
if (l2cache_pmu->num_pmus == 0) {
dev_err(&pdev->dev, "No hardware L2 cache PMUs found\n"); return -ENODEV;
}
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.