switch (etype) { case SBI_PMU_EVENT_TYPE_HW:
type = PERF_TYPE_HARDWARE; break; case SBI_PMU_EVENT_TYPE_CACHE:
type = PERF_TYPE_HW_CACHE; break; case SBI_PMU_EVENT_TYPE_RAW: case SBI_PMU_EVENT_TYPE_FW:
type = PERF_TYPE_RAW; break; default: break;
}
staticint kvm_pmu_get_programmable_pmc_index(struct kvm_pmu *kvpmu, unsignedlong eidx, unsignedlong cbase, unsignedlong cmask)
{ int ctr_idx = -1; int i, pmc_idx; int min, max;
if (kvm_pmu_is_fw_event(eidx)) { /* Firmware counters are mapped 1:1 starting from num_hw_ctrs for simplicity */
min = kvpmu->num_hw_ctrs;
max = min + kvpmu->num_fw_ctrs;
} else { /* First 3 counters are reserved for fixed counters */
min = 3;
max = kvpmu->num_hw_ctrs;
}
for_each_set_bit(i, &cmask, BITS_PER_LONG) {
pmc_idx = i + cbase; if ((pmc_idx >= min && pmc_idx < max) &&
!test_bit(pmc_idx, kvpmu->pmc_in_use)) {
ctr_idx = pmc_idx; break;
}
}
staticint kvm_pmu_validate_counter_mask(struct kvm_pmu *kvpmu, unsignedlong ctr_base, unsignedlong ctr_mask)
{ /* Make sure the we have a valid counter mask requested from the caller */ if (!ctr_mask || (ctr_base + __fls(ctr_mask) >= kvm_pmu_num_counters(kvpmu))) return -EINVAL;
/* * Stop the event counting by directly accessing the perf_event. * Otherwise, this needs to deferred via a workqueue. * That will introduce skew in the counter value because the actual * physical counter would start after returning from this function. * It will be stopped again once the workqueue is scheduled
*/
rpmu->pmu.stop(perf_event, PERF_EF_UPDATE);
/* * The hw counter would start automatically when this function returns. * Thus, the host may continue to interrupt and inject it to the guest * even without the guest configuring the next event. Depending on the hardware * the host may have some sluggishness only if privilege mode filtering is not * available. In an ideal world, where qemu is not the only capable hardware, * this can be removed. * FYI: ARM64 does this way while x86 doesn't do anything as such. * TODO: Should we keep it for RISC-V ?
*/
period = -(local64_read(&perf_event->count));
kvm_pmu_release_perf_event(pmc);
attr->config = kvm_pmu_get_perf_event_config(eidx, evtdata); if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE) { //TODO: Do we really want to clear the value in hardware counter
pmc->counter_val = 0;
}
/* * Set the default sample_period for now. The guest specified value * will be updated in the start call.
*/
attr->sample_period = kvm_pmu_get_sample_period(pmc);
if (!kvpmu || fid >= SBI_PMU_FW_MAX) return -EINVAL;
fevent = &kvpmu->fw_event[fid]; if (fevent->started)
fevent->value++;
return 0;
}
int kvm_riscv_vcpu_pmu_read_hpm(struct kvm_vcpu *vcpu, unsignedint csr_num, unsignedlong *val, unsignedlong new_val, unsignedlong wr_mask)
{ struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); int cidx, ret = KVM_INSN_CONTINUE_NEXT_SEPC;
if (!kvpmu || !kvpmu->init_done) { /* * In absence of sscofpmf in the platform, the guest OS may use * the legacy PMU driver to read cycle/instret. In that case, * just return 0 to avoid any illegal trap. However, any other * hpmcounter access should result in illegal trap as they must * be access through SBI PMU only.
*/ if (csr_num == CSR_CYCLE || csr_num == CSR_INSTRET) {
*val = 0; return ret;
} else { return KVM_INSN_ILLEGAL_TRAP;
}
}
/* The counter CSR are read only. Thus, any write should result in illegal traps */ if (wr_mask) return KVM_INSN_ILLEGAL_TRAP;
cidx = csr_num - CSR_CYCLE;
if (pmu_ctr_read(vcpu, cidx, val) < 0) return KVM_INSN_ILLEGAL_TRAP;
if (snap_flag_set) { if (kvpmu->snapshot_addr == INVALID_GPA) {
sbiret = SBI_ERR_NO_SHMEM; goto out;
} if (kvm_vcpu_read_guest(vcpu, kvpmu->snapshot_addr, kvpmu->sdata, sizeof(struct riscv_pmu_snapshot_data))) {
pr_warn("Unable to read snapshot shared memory while starting counters\n");
sbiret = SBI_ERR_FAILURE; goto out;
}
} /* Start the counters that have been configured and requested by the guest */
for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
pmc_index = i + ctr_base; if (!test_bit(pmc_index, kvpmu->pmc_in_use)) continue; /* The guest started the counter again. Reset the overflow status */
clear_bit(pmc_index, kvpmu->pmc_overflown);
pmc = &kvpmu->pmc[pmc_index]; if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE) {
pmc->counter_val = ival;
} elseif (snap_flag_set) { /* The counter index in the snapshot are relative to the counter base */
pmc->counter_val = kvpmu->sdata->ctr_values[i];
}
if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
fevent_code = get_event_code(pmc->event_idx); if (fevent_code >= SBI_PMU_FW_MAX) {
sbiret = SBI_ERR_INVALID_PARAM; goto out;
}
/* Check if the counter was already started for some reason */ if (kvpmu->fw_event[fevent_code].started) {
sbiret = SBI_ERR_ALREADY_STARTED; continue;
}
/* Stop the counters that have been configured and requested by the guest */
for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
pmc_index = i + ctr_base; if (!test_bit(pmc_index, kvpmu->pmc_in_use)) continue;
pmc = &kvpmu->pmc[pmc_index]; if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
fevent_code = get_event_code(pmc->event_idx); if (fevent_code >= SBI_PMU_FW_MAX) {
sbiret = SBI_ERR_INVALID_PARAM; goto out;
}
if (!kvpmu->fw_event[fevent_code].started)
sbiret = SBI_ERR_ALREADY_STOPPED;
if (flags & SBI_PMU_STOP_FLAG_RESET) /* Release the counter if this is a reset request */
kvm_pmu_release_perf_event(pmc);
} else {
sbiret = SBI_ERR_INVALID_PARAM;
}
if (snap_flag_set && !sbiret) { if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW)
pmc->counter_val = kvpmu->fw_event[fevent_code].value; elseif (pmc->perf_event)
pmc->counter_val += perf_event_read_value(pmc->perf_event,
&enabled, &running); /* * The counter and overflow indicies in the snapshot region are w.r.to * cbase. Modify the set bit in the counter mask instead of the pmc_index * which indicates the absolute counter index.
*/ if (test_bit(pmc_index, kvpmu->pmc_overflown))
kvpmu->sdata->ctr_overflow_mask |= BIT(i);
kvpmu->sdata->ctr_values[i] = pmc->counter_val;
shmem_needs_update = true;
}
if (flags & SBI_PMU_STOP_FLAG_RESET) {
pmc->event_idx = SBI_PMU_EVENT_IDX_INVALID;
clear_bit(pmc_index, kvpmu->pmc_in_use);
clear_bit(pmc_index, kvpmu->pmc_overflown); if (snap_flag_set) { /* * Only clear the given counter as the caller is responsible to * validate both the overflow mask and configured counters.
*/
kvpmu->sdata->ctr_overflow_mask &= ~BIT(i);
shmem_needs_update = true;
}
}
}
if (shmem_needs_update)
kvm_vcpu_write_guest(vcpu, kvpmu->snapshot_addr, kvpmu->sdata, sizeof(struct riscv_pmu_snapshot_data));
out:
retdata->err_val = sbiret;
return 0;
}
int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsignedlong ctr_base, unsignedlong ctr_mask, unsignedlong flags, unsignedlong eidx, u64 evtdata, struct kvm_vcpu_sbi_return *retdata)
{ int ctr_idx, sbiret = 0; long ret; bool is_fevent; unsignedlong event_code;
u32 etype = kvm_pmu_get_perf_event_type(eidx); struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); struct kvm_pmc *pmc = NULL; struct perf_event_attr attr = {
.type = etype,
.size = sizeof(struct perf_event_attr),
.pinned = true,
.disabled = true, /* * It should never reach here if the platform doesn't support the sscofpmf * extension as mode filtering won't work without it.
*/
.exclude_host = true,
.exclude_hv = true,
.exclude_user = !!(flags & SBI_PMU_CFG_FLAG_SET_UINH),
.exclude_kernel = !!(flags & SBI_PMU_CFG_FLAG_SET_SINH),
.config1 = RISCV_PMU_CONFIG1_GUEST_EVENTS,
};
/* * SKIP_MATCH flag indicates the caller is aware of the assigned counter * for this event. Just do a sanity check if it already marked used.
*/ if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) { if (!test_bit(ctr_base + __ffs(ctr_mask), kvpmu->pmc_in_use)) {
sbiret = SBI_ERR_FAILURE; goto out;
}
ctr_idx = ctr_base + __ffs(ctr_mask);
} else {
ctr_idx = pmu_get_pmc_index(kvpmu, eidx, ctr_base, ctr_mask); if (ctr_idx < 0) {
sbiret = SBI_ERR_NOT_SUPPORTED; goto out;
}
}
pmc = &kvpmu->pmc[ctr_idx];
pmc->idx = ctr_idx;
if (is_fevent) { if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
kvpmu->fw_event[event_code].started = true;
} else {
ret = kvm_pmu_create_perf_event(pmc, &attr, flags, eidx, evtdata); if (ret) {
sbiret = SBI_ERR_NOT_SUPPORTED; goto out;
}
}
/* * PMU functionality should be only available to guests if privilege mode * filtering is available in the host. Otherwise, guest will always count * events while the execution is in hypervisor mode.
*/ if (!riscv_isa_extension_available(NULL, SSCOFPMF)) return;
ret = riscv_pmu_get_hpm_info(&hpm_width, &num_hw_ctrs); if (ret < 0 || !hpm_width || !num_hw_ctrs) return;
/* * Increase the number of hardware counters to offset the time counter.
*/
kvpmu->num_hw_ctrs = num_hw_ctrs + 1;
kvpmu->num_fw_ctrs = SBI_PMU_FW_MAX;
memset(&kvpmu->fw_event, 0, SBI_PMU_FW_MAX * sizeof(struct kvm_fw_event));
kvpmu->snapshot_addr = INVALID_GPA;
if (kvpmu->num_hw_ctrs > RISCV_KVM_MAX_HW_CTRS) {
pr_warn_once("Limiting the hardware counters to 32 as specified by the ISA");
kvpmu->num_hw_ctrs = RISCV_KVM_MAX_HW_CTRS;
}
/* * There is no correlation between the logical hardware counter and virtual counters. * However, we need to encode a hpmcounter CSR in the counter info field so that * KVM can trap n emulate the read. This works well in the migration use case as * KVM doesn't care if the actual hpmcounter is available in the hardware or not.
*/ for (i = 0; i < kvm_pmu_num_counters(kvpmu); i++) { /* TIME CSR shouldn't be read from perf interface */ if (i == 1) continue;
pmc = &kvpmu->pmc[i];
pmc->idx = i;
pmc->event_idx = SBI_PMU_EVENT_IDX_INVALID;
pmc->vcpu = vcpu; if (i < kvpmu->num_hw_ctrs) {
pmc->cinfo.type = SBI_PMU_CTR_TYPE_HW; if (i < 3) /* CY, IR counters */
pmc->cinfo.width = 63; else
pmc->cinfo.width = hpm_width; /* * The CSR number doesn't have any relation with the logical * hardware counters. The CSR numbers are encoded sequentially * to avoid maintaining a map between the virtual counter * and CSR number.
*/
pmc->cinfo.csr = CSR_CYCLE + i;
} else {
pmc->cinfo.type = SBI_PMU_CTR_TYPE_FW;
pmc->cinfo.width = 63;
}
}
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.