staticint amd_core_hw_config(struct perf_event *event)
{ if (event->attr.exclude_host && event->attr.exclude_guest) /* * When HO == GO == 1 the hardware treats that as GO == HO == 0 * and will count in both modes. We don't want to count in that * case so we emulate no-counting by setting US = OS = 0.
*/
event->hw.config &= ~(ARCH_PERFMON_EVENTSEL_USR |
ARCH_PERFMON_EVENTSEL_OS); elseif (event->attr.exclude_host)
event->hw.config |= AMD64_EVENTSEL_GUESTONLY; elseif (event->attr.exclude_guest)
event->hw.config |= AMD64_EVENTSEL_HOSTONLY;
if ((x86_pmu.flags & PMU_FL_PAIR) && amd_is_pair_event_code(&event->hw))
event->hw.flags |= PERF_X86_EVENT_PAIR;
if (has_branch_stack(event)) return static_call(amd_pmu_branch_hw_config)(event);
/* * need to scan whole list because event may not have * been assigned during scheduling * * no race condition possible because event can only * be removed on one CPU at a time AND PMU is disabled * when we come here
*/
for_each_set_bit(i, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { struct perf_event *tmp = event;
if (try_cmpxchg(nb->owners + i, &tmp, NULL)) break;
}
}
/* * AMD64 NorthBridge events need special treatment because * counter access needs to be synchronized across all cores * of a package. Refer to BKDG section 3.12 * * NB events are events measuring L3 cache, Hypertransport * traffic. They are identified by an event code >= 0xe00. * They measure events on the NorthBride which is shared * by all cores on a package. NB events are counted on a * shared set of counters. When a NB event is programmed * in a counter, the data actually comes from a shared * counter. Thus, access to those counters needs to be * synchronized. * * We implement the synchronization such that no two cores * can be measuring NB events using the same counters. Thus, * we maintain a per-NB allocation table. The available slot * is propagated using the event_constraint structure. * * We provide only one choice for each NB event based on * the fact that only NB events have restrictions. Consequently, * if a counter is available, there is a guarantee the NB event * will be assigned to it. If no slot is available, an empty * constraint is returned and scheduling will eventually fail * for this event. * * Note that all cores attached the same NB compete for the same * counters to host NB events, this is why we use atomic ops. Some * multi-chip CPUs may have more than one NB. * * Given that resources are allocated (cmpxchg), they must be * eventually freed for others to use. This is accomplished by * calling __amd_put_nb_event_constraints() * * Non NB events are not impacted by this restriction.
*/ staticstruct event_constraint *
__amd_get_nb_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event, struct event_constraint *c)
{ struct hw_perf_event *hwc = &event->hw; struct amd_nb *nb = cpuc->amd_nb; struct perf_event *old; int idx, new = -1;
if (!c)
c = &unconstrained;
if (cpuc->is_fake) return c;
/* * detect if already present, if so reuse * * cannot merge with actual allocation * because of possible holes * * event can already be present yet not assigned (in hwc->idx) * because of successive calls to x86_schedule_events() from * hw_perf_group_sched_in() without hw_perf_enable()
*/
for_each_set_bit(idx, c->idxmsk, x86_pmu_max_num_counters(NULL)) { if (new == -1 || hwc->idx == idx) /* assign free slot, prefer hwc->idx */
old = cmpxchg(nb->owners + idx, NULL, event); elseif (nb->owners[idx] == event) /* event already present */
old = event; else continue;
if (old && old != event) continue;
/* reassign to this slot */ if (new != -1)
cmpxchg(nb->owners + new, event, NULL); new = idx;
/* PerfCntrGlobalStatus is read-only */
rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, status);
return status;
}
staticinlinevoid amd_pmu_ack_global_status(u64 status)
{ /* * PerfCntrGlobalStatus is read-only but an overflow acknowledgment * mechanism exists; writing 1 to a bit in PerfCntrGlobalStatusClr * clears the same bit in PerfCntrGlobalStatus
*/
/* * When a PMC counter overflows, an NMI is used to process the event and * reset the counter. NMI latency can result in the counter being updated * before the NMI can run, which can result in what appear to be spurious * NMIs. This function is intended to wait for the NMI to run and reset * the counter to avoid possible unhandled NMI messages.
*/ #define OVERFLOW_WAIT_COUNT 50
/* * Wait for the counter to be reset if it has overflowed. This loop * should exit very, very quickly, but just in case, don't wait * forever...
*/ for (i = 0; i < OVERFLOW_WAIT_COUNT; i++) { if (!static_call(amd_pmu_test_overflow)(idx)) break;
/* Might be in IRQ context, so can't sleep */
udelay(1);
}
}
staticvoid amd_pmu_check_overflow(void)
{ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx;
/* * This shouldn't be called from NMI context, but add a safeguard here * to return, since if we're in NMI context we can't wait for an NMI * to reset an overflowed counter value.
*/ if (in_nmi()) return;
/* * Check each counter for overflow and wait for it to be reset by the * NMI if it has overflowed. This relies on the fact that all active * counters are always enabled when this function is called and * ARCH_PERFMON_EVENTSEL_INT is always set.
*/
for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { if (!test_bit(idx, cpuc->active_mask)) continue;
for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { /* only activate events which are marked as active */ if (!test_bit(idx, cpuc->active_mask)) continue;
/* * Testing cpu_hw_events.enabled should be skipped in this case unlike * in x86_pmu_enable_event(). * * Since cpu_hw_events.enabled is set only after returning from * x86_pmu_start(), the PMCs must be programmed and kept ready. * Counting starts only after x86_pmu_enable_all() is called.
*/
__x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
}
/* * This can be called from NMI context (via x86_pmu_stop). The counter * may have overflowed, but either way, we'll never see it get reset * by the NMI if we're already in the NMI. And the NMI latency support * below will take care of any pending NMI that might have been * generated by the overflow.
*/ if (in_nmi()) return;
staticvoid amd_pmu_del_event(struct perf_event *event)
{ if (needs_branch_stack(event))
static_call(amd_pmu_branch_del)(event);
}
/* * Because of NMI latency, if multiple PMC counters are active or other sources * of NMIs are received, the perf NMI handler can handle one or more overflowed * PMC counters outside of the NMI associated with the PMC overflow. If the NMI * doesn't arrive at the LAPIC in time to become a pending NMI, then the kernel * back-to-back NMI support won't be active. This PMC handler needs to take into * account that this can occur, otherwise this could result in unknown NMI * messages being issued. Examples of this is PMC overflow while in the NMI * handler when multiple PMCs are active or PMC overflow while handling some * other source of an NMI. * * Attempt to mitigate this by creating an NMI window in which un-handled NMIs * received during this window will be claimed. This prevents extending the * window past when it is possible that latent NMIs should be received. The * per-CPU perf_nmi_tstamp will be set to the window end time whenever perf has * handled a counter. When an un-handled NMI is received, it will be claimed * only if arriving within that window.
*/ staticinlineint amd_pmu_adjust_nmi_window(int handled)
{ /* * If a counter was handled, record a timestamp such that un-handled * NMIs will be claimed if arriving within that window.
*/ if (handled) {
this_cpu_write(perf_nmi_tstamp, jiffies + perf_nmi_window);
return handled;
}
if (time_after(jiffies, this_cpu_read(perf_nmi_tstamp))) return NMI_DONE;
return NMI_HANDLED;
}
staticint amd_pmu_handle_irq(struct pt_regs *regs)
{ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int handled; int pmu_enabled;
/* * Save the PMU state. * It needs to be restored when leaving the handler.
*/
pmu_enabled = cpuc->enabled;
cpuc->enabled = 0;
amd_brs_disable_all();
/* Drain BRS is in use (could be inactive) */ if (cpuc->lbr_users)
amd_brs_drain();
/* Process any counter overflows */
handled = x86_pmu_handle_irq(regs);
cpuc->enabled = pmu_enabled; if (pmu_enabled)
amd_brs_enable_all();
return amd_pmu_adjust_nmi_window(handled);
}
/* * AMD-specific callback invoked through perf_snapshot_branch_stack static * call, defined in include/linux/perf_event.h. See its definition for API * details. It's up to caller to provide enough space in *entries* to fit all * LBR records, otherwise returned result will be truncated to *cnt* entries.
*/ staticint amd_pmu_v2_snapshot_branch_stack(struct perf_branch_entry *entries, unsignedint cnt)
{ struct cpu_hw_events *cpuc; unsignedlong flags;
/* * The sequence of steps to freeze LBR should be completely inlined * and contain no branches to minimize contamination of LBR snapshot
*/
local_irq_save(flags);
amd_pmu_core_disable_all();
__amd_pmu_lbr_disable();
/* * Save the PMU state as it needs to be restored when leaving the * handler
*/
pmu_enabled = cpuc->enabled;
cpuc->enabled = 0;
/* Stop counting but do not disable LBR */
amd_pmu_core_disable_all();
status = amd_pmu_get_global_status();
/* Check if any overflows are pending */ if (!status) goto done;
/* Read branch records */ if (x86_pmu.lbr_nr) {
amd_pmu_lbr_read();
status &= ~GLOBAL_STATUS_LBRS_FROZEN;
}
reserved = status & ~amd_pmu_global_cntr_mask; if (reserved)
pr_warn_once("Reserved PerfCntrGlobalStatus bits are set (0x%llx), please consider updating microcode\n",
reserved);
/* Clear any reserved bits set by buggy microcode */
status &= amd_pmu_global_cntr_mask;
for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { if (!test_bit(idx, cpuc->active_mask)) continue;
/* * It should never be the case that some overflows are not handled as * the corresponding PMCs are expected to be inactive according to the * active_mask
*/ if (status > 0) {
prev_bits = atomic64_fetch_or(status, &status_warned); // A new bit was set for the very first time.
new_bits = status & ~prev_bits;
WARN(new_bits, "New overflows for inactive PMCs: %llx\n", new_bits);
}
/* Clear overflow and freeze bits */
amd_pmu_ack_global_status(~status);
/* * Unmasking the LVTPC is not required as the Mask (M) bit of the LVT * PMI entry is not set by the local APIC when a PMC overflow occurs
*/
inc_irq_stat(apic_perf_irqs);
done:
cpuc->enabled = pmu_enabled;
/* Resume counting only if PMU is active */ if (pmu_enabled)
amd_pmu_core_enable_all();
return amd_pmu_adjust_nmi_window(handled);
}
staticstruct event_constraint *
amd_get_event_constraints(struct cpu_hw_events *cpuc, int idx, struct perf_event *event)
{ /* * if not NB event or no NB, then no constraints
*/ if (!(amd_has_nb(cpuc) && amd_is_nb_event(&event->hw))) return &unconstrained;
/* * AMD family 15h event code/PMC mappings: * * type = event_code & 0x0F0: * * 0x000 FP PERF_CTL[5:3] * 0x010 FP PERF_CTL[5:3] * 0x020 LS PERF_CTL[5:0] * 0x030 LS PERF_CTL[5:0] * 0x040 DC PERF_CTL[5:0] * 0x050 DC PERF_CTL[5:0] * 0x060 CU PERF_CTL[2:0] * 0x070 CU PERF_CTL[2:0] * 0x080 IC/DE PERF_CTL[2:0] * 0x090 IC/DE PERF_CTL[2:0] * 0x0A0 --- * 0x0B0 --- * 0x0C0 EX/LS PERF_CTL[5:0] * 0x0D0 DE PERF_CTL[2:0] * 0x0E0 NB NB_PERF_CTL[3:0] * 0x0F0 NB NB_PERF_CTL[3:0] * * Exceptions: * * 0x000 FP PERF_CTL[3], PERF_CTL[5:3] (*) * 0x003 FP PERF_CTL[3] * 0x004 FP PERF_CTL[3], PERF_CTL[5:3] (*) * 0x00B FP PERF_CTL[3] * 0x00D FP PERF_CTL[3] * 0x023 DE PERF_CTL[2:0] * 0x02D LS PERF_CTL[3] * 0x02E LS PERF_CTL[3,0] * 0x031 LS PERF_CTL[2:0] (**) * 0x043 CU PERF_CTL[2:0] * 0x045 CU PERF_CTL[2:0] * 0x046 CU PERF_CTL[2:0] * 0x054 CU PERF_CTL[2:0] * 0x055 CU PERF_CTL[2:0] * 0x08F IC PERF_CTL[0] * 0x187 DE PERF_CTL[0] * 0x188 DE PERF_CTL[0] * 0x0DB EX PERF_CTL[5:0] * 0x0DC LS PERF_CTL[5:0] * 0x0DD LS PERF_CTL[5:0] * 0x0DE LS PERF_CTL[5:0] * 0x0DF LS PERF_CTL[5:0] * 0x1C0 EX PERF_CTL[5:3] * 0x1D6 EX PERF_CTL[5:0] * 0x1D8 EX PERF_CTL[5:0] * * (*) depending on the umask all FPU counters may be used * (**) only one unitmask enabled at a time
*/
switch (event_code & AMD_EVENT_TYPE_MASK) { case AMD_EVENT_FP: switch (event_code) { case 0x000: if (!(hwc->config & 0x0000F000ULL)) break; if (!(hwc->config & 0x00000F00ULL)) break; return &amd_f15_PMC3; case 0x004: if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1) break; return &amd_f15_PMC3; case 0x003: case 0x00B: case 0x00D: return &amd_f15_PMC3;
} return &amd_f15_PMC53; case AMD_EVENT_LS: case AMD_EVENT_DC: case AMD_EVENT_EX_LS: switch (event_code) { case 0x023: case 0x043: case 0x045: case 0x046: case 0x054: case 0x055: return &amd_f15_PMC20; case 0x02D: return &amd_f15_PMC3; case 0x02E: return &amd_f15_PMC30; case 0x031: if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1) return &amd_f15_PMC20; return &emptyconstraint; case 0x1C0: return &amd_f15_PMC53; default: return &amd_f15_PMC50;
} case AMD_EVENT_CU: case AMD_EVENT_IC_DE: case AMD_EVENT_DE: switch (event_code) { case 0x08F: case 0x187: case 0x188: return &amd_f15_PMC0; case 0x0DB ... 0x0DF: case 0x1D6: case 0x1D8: return &amd_f15_PMC50; default: return &amd_f15_PMC20;
} case AMD_EVENT_NB: /* moved to uncore.c */ return &emptyconstraint; default: return &emptyconstraint;
}
}
/* * Because of the way BRS operates with an inactive and active phases, and * the link to one counter, it is not possible to have two events using BRS * scheduled at the same time. There would be an issue with enforcing the * period of each one and given that the BRS saturates, it would not be possible * to guarantee correlated content for all events. Therefore, in situations * where multiple events want to use BRS, the kernel enforces mutual exclusion. * Exclusion is enforced by choosing only one counter for events using BRS. * The event scheduling logic will then automatically multiplex the * events and ensure that at most one event is actively using BRS. * * The BRS counter could be any counter, but there is no constraint on Fam19h, * therefore all counters are equal and thus we pick the first one: PMC0
*/ staticstruct event_constraint amd_fam19h_brs_cntr0_constraint =
EVENT_CONSTRAINT(0, 0x1, AMD64_RAW_EVENT_MASK);
/* * In case BRS is used with an event requiring a counter pair, * the kernel allows it but only on counter 0 & 1 to enforce * multiplexing requiring to protect BRS in case of multiple * BRS users
*/ if (amd_is_pair_event_code(hwc)) { return has_brs ? &amd_fam19h_brs_pair_cntr0_constraint
: &pair_constraint;
}
if (has_brs) return &amd_fam19h_brs_cntr0_constraint;
staticvoid amd_pmu_limit_period(struct perf_event *event, s64 *left)
{ /* * Decrease period by the depth of the BRS feature to get the last N * taken branches and approximate the desired period
*/ if (has_branch_stack(event) && *left > x86_pmu.lbr_nr)
*left -= x86_pmu.lbr_nr;
}
/* * AMD Core perfctr has separate MSRs for the NB events, see * the amd/uncore.c driver.
*/
x86_pmu.amd_nb_constraints = 0;
if (boot_cpu_data.x86 == 0x15) {
pr_cont("Fam15h ");
x86_pmu.get_event_constraints = amd_get_event_constraints_f15h;
} if (boot_cpu_data.x86 >= 0x17) {
pr_cont("Fam17h+ "); /* * Family 17h and compatibles have constraints for Large * Increment per Cycle events: they may only be assigned an * even numbered counter that has a consecutive adjacent odd * numbered counter following it.
*/ for (i = 0; i < x86_pmu_max_num_counters(NULL) - 1; i += 2)
even_ctr_mask |= BIT_ULL(i);
/* * put_event_constraints callback same as Fam17h, set above
*/
/* branch sampling must be stopped when entering low power */
amd_brs_lopwr_init();
}
x86_pmu.attr_update = amd_attr_update;
pr_cont("core perfctr, "); return 0;
}
__init int amd_pmu_init(void)
{ int ret;
/* Performance-monitoring supported from K7 and later: */ if (boot_cpu_data.x86 < 6) return -ENODEV;
x86_pmu = amd_pmu;
ret = amd_core_pmu_init(); if (ret) return ret;
if (num_possible_cpus() == 1) { /* * No point in allocating data structures to serialize * against other CPUs, when there is only the one CPU.
*/
x86_pmu.amd_nb_constraints = 0;
}
staticinlinevoid amd_pmu_reload_virt(void)
{ if (x86_pmu.version >= 2) { /* * Clear global enable bits, reprogram the PERF_CTL * registers with updated perf_ctr_virt_mask and then * set global enable bits once again
*/
amd_pmu_v2_disable_all();
amd_pmu_enable_all(0);
amd_pmu_v2_enable_all(0); return;
}
/* * We only mask out the Host-only bit so that host-only counting works * when SVM is disabled. If someone sets up a guest-only counter when * SVM is disabled the Guest-only bits still gets set and the counter * will not count anything.
*/
cpuc->perf_ctr_virt_mask = AMD64_EVENTSEL_HOSTONLY;
/* Reload all events */
amd_pmu_reload_virt();
}
EXPORT_SYMBOL_GPL(amd_pmu_disable_virt);
Messung V0.5
¤ Dauer der Verarbeitung: 0.5 Sekunden
(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.