/* The maximum number of PMCs on any Alpha CPU whatsoever. */ #define MAX_HWEVENTS 3 #define PMC_NO_INDEX -1
/* For tracking PMCs and the hw events they monitor on each CPU. */ struct cpu_hw_events { int enabled; /* Number of events scheduled; also number entries valid in arrays below. */ int n_events; /* Number events added since last hw_perf_disable(). */ int n_added; /* Events currently scheduled. */ struct perf_event *event[MAX_HWEVENTS]; /* Event type of each scheduled event. */ unsignedlong evtype[MAX_HWEVENTS]; /* Current index of each scheduled event; if not yet determined *containsPMC_NO_INDEX.
*/ int current_idx[MAX_HWEVENTS]; /* The active PMCs' config for easy use with wrperfmon(). */ unsignedlong config; /* The active counters' indices for easy use with wrperfmon(). */ unsignedlong idx_mask;
};
DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
/* *AstructuretoholdthedescriptionofthePMCsavailableonaparticular *typeofAlphaCPU.
*/ struct alpha_pmu_t { /* Mapping of the perf system hw event types to indigenous event types */ constint *event_map; /* The number of entries in the event_map */ int max_events; /* The number of PMCs on this Alpha */ int num_pmcs; /* *AllPMCcountersresideintheIBOXregisterPCTR.Thisisthe *LSBofthecounter.
*/ int pmc_count_shift[MAX_HWEVENTS]; /* *ThemaskthatisolatesthePMCbitswhentheLSBofthecounter *isshiftedtobit0.
*/ unsignedlong pmc_count_mask[MAX_HWEVENTS]; /* The maximum period the PMC can count. */ unsignedlong pmc_max_period[MAX_HWEVENTS]; /* *Themaximumvaluethatmaybewrittentothecounterdueto *hardwarerestrictionsispmc_max_period-pmc_left.
*/ long pmc_left[3]; /* Subroutine for allocation of PMCs. Enforces constraints. */ int (*check_constraints)(struct perf_event **, unsignedlong *, int); /* Subroutine for checking validity of a raw event for this PMU. */ int (*raw_event_valid)(u64 config);
};
if (evtype[0] == EV67_MBOXREPLAY || evtype[1] == EV67_MBOXREPLAY) { /* MBOX replay traps must be on PMC 1 */
idx0 = (evtype[0] == EV67_MBOXREPLAY) ? 1 : 0; /* Only cycles can accompany MBOX replay traps */ if (evtype[idx0] == EV67_CYCLES) {
config = EV67_PCTR_CYCLES_MBOX; goto success;
}
}
if (evtype[0] == EV67_BCACHEMISS || evtype[1] == EV67_BCACHEMISS) { /* Bcache misses must be on PMC 1 */
idx0 = (evtype[0] == EV67_BCACHEMISS) ? 1 : 0; /* Only instructions can accompany Bcache misses */ if (evtype[idx0] == EV67_INSTRUCTIONS) {
config = EV67_PCTR_INSTR_BCACHEMISS; goto success;
}
}
if (evtype[0] == EV67_INSTRUCTIONS || evtype[1] == EV67_INSTRUCTIONS) { /* Instructions must be on PMC 0 */
idx0 = (evtype[0] == EV67_INSTRUCTIONS) ? 0 : 1; /* By this point only cycles can accompany instructions */ if (evtype[idx0^1] == EV67_CYCLES) {
config = EV67_PCTR_INSTR_CYCLES; goto success;
}
}
/* Otherwise, darn it, there is a conflict. */ return -1;
val = wrperfmon(PERFMON_CMD_READ, 0);
val >>= alpha_pmu->pmc_count_shift[idx];
val &= alpha_pmu->pmc_count_mask[idx]; return val;
}
/* Set a new period to sample over */ staticint alpha_perf_event_set_period(struct perf_event *event, struct hw_perf_event *hwc, int idx)
{ long left = local64_read(&hwc->period_left); long period = hwc->sample_period; int ret = 0;
if (unlikely(left <= -period)) {
left = period;
local64_set(&hwc->period_left, left);
hwc->last_period = period;
ret = 1;
}
if (unlikely(left <= 0)) {
left += period;
local64_set(&hwc->period_left, left);
hwc->last_period = period;
ret = 1;
}
/* *Hardwarerestrictionsrequirethatthecountersmustnotbe *writtenwithvaluesthataretooclosetothemaximumperiod.
*/ if (unlikely(left < alpha_pmu->pmc_left[idx]))
left = alpha_pmu->pmc_left[idx];
if (left > (long)alpha_pmu->pmc_max_period[idx])
left = alpha_pmu->pmc_max_period[idx];
/* It is possible on very rare occasions that the PMC has overflowed *buttheinterruptisyettocome.Detectandfixthissituation.
*/ if (unlikely(delta < 0)) {
delta += alpha_pmu->pmc_max_period[idx] + 1;
}
/* Default to error to be returned */
ret = -EAGAIN;
/* Insert event on to PMU and if successful modify ret to valid return */
n0 = cpuc->n_events; if (n0 < alpha_pmu->num_pmcs) {
cpuc->event[n0] = event;
cpuc->evtype[n0] = event->hw.event_base;
cpuc->current_idx[n0] = PMC_NO_INDEX;
if (!alpha_check_constraints(cpuc->event, cpuc->evtype, n0+1)) {
cpuc->n_events++;
cpuc->n_added++;
ret = 0;
}
}
hwc->state = PERF_HES_UPTODATE; if (!(flags & PERF_EF_START))
hwc->state |= PERF_HES_STOPPED;
/* Get cpu type from HW */
cpu = (struct percpu_struct *)((char *)hwrpb + hwrpb->processor_offset);
cputype = cpu->type & 0xffffffff; /* Include all of EV67, EV68, EV7, EV79 and EV69 as supported. */ return (cputype >= EV67_CPU) && (cputype <= EV69_CPU);
}
staticvoid hw_perf_event_destroy(struct perf_event *event)
{ /* Nothing to be done! */ return;
}
staticint __hw_perf_event_init(struct perf_event *event)
{ struct perf_event_attr *attr = &event->attr; struct hw_perf_event *hwc = &event->hw; struct perf_event *evts[MAX_HWEVENTS]; unsignedlong evtypes[MAX_HWEVENTS]; int idx_rubbish_bin[MAX_HWEVENTS]; int ev; int n;
/* We only support a limited range of HARDWARE event types with one *onlyprogrammableviaaRAWeventtype.
*/ if (attr->type == PERF_TYPE_HARDWARE) { if (attr->config >= alpha_pmu->max_events) return -EINVAL;
ev = alpha_pmu->event_map[attr->config];
} elseif (attr->type == PERF_TYPE_HW_CACHE) { return -EOPNOTSUPP;
} elseif (attr->type == PERF_TYPE_RAW) { if (!alpha_pmu->raw_event_valid(attr->config)) return -EINVAL;
ev = attr->config;
} else { return -EOPNOTSUPP;
}
/* Collect events in a group together suitable for calling *alpha_check_constraints()toverifythatthegroupasawholecan *bescheduledontothePMU.
*/
n = 0; if (event->group_leader != event) {
n = collect_events(event->group_leader,
alpha_pmu->num_pmcs - 1,
evts, evtypes, idx_rubbish_bin); if (n < 0) return -EINVAL;
}
evtypes[n] = hwc->event_base;
evts[n] = event;
if (alpha_check_constraints(evts, evtypes, n + 1)) return -EINVAL;
/* Indicate that PMU config and idx are yet to be determined. */
hwc->config_base = 0;
hwc->idx = PMC_NO_INDEX;
/* *MainentrypointtoinitialiseaHWperformanceevent.
*/ staticint alpha_pmu_event_init(struct perf_event *event)
{ /* does not support taken branch sampling */ if (has_branch_stack(event)) return -EOPNOTSUPP;
switch (event->attr.type) { case PERF_TYPE_RAW: case PERF_TYPE_HARDWARE: case PERF_TYPE_HW_CACHE: break;
default: return -ENOENT;
}
if (!alpha_pmu) return -ENODEV;
/* Do the real initialisation work. */ return __hw_perf_event_init(event);
}
/* Completely counting through the PMC's period to trigger a new PMC *overflowinterruptwhileinthisinterruptroutineisutterly *disastrous!TheEV6andEV67countersaresufficientlylargeto *preventthisbuttobereallysuredisablethePMCs.
*/
wrperfmon(PERFMON_CMD_DISABLE, cpuc->idx_mask);
/* la_ptr is the counter that overflowed. */ if (unlikely(la_ptr >= alpha_pmu->num_pmcs)) { /* This should never occur! */
irq_err_count++;
pr_warn("PMI: silly index %ld\n", la_ptr);
wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask); return;
}
idx = la_ptr;
for (j = 0; j < cpuc->n_events; j++) { if (cpuc->current_idx[j] == idx) break;
}
if (unlikely(j == cpuc->n_events)) { /* This can occur if the event is disabled right on a PMC overflow. */
wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask); return;
}
event = cpuc->event[j];
if (unlikely(!event)) { /* This should never occur! */
irq_err_count++;
pr_warn("PMI: No event at index %d!\n", idx);
wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask); return;
}
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.