// SPDX-License-Identifier: GPL-2.0 /* * RISC-V performance counter support. * * Copyright (C) 2021 Western Digital Corporation or its affiliates. * * This code is based on ARM perf event code which is in turn based on * sparc64 and x86 code.
*/
/* Allow user mode access by default */ staticint sysctl_perf_user_access __read_mostly = SYSCTL_USER_ACCESS;
/* * RISC-V doesn't have heterogeneous harts yet. This need to be part of * per_cpu in case of harts with different pmu counters
*/ staticunion sbi_pmu_ctr_info *pmu_ctr_list; staticbool riscv_pmu_use_irq; staticunsignedint riscv_pmu_irq_num; staticunsignedint riscv_pmu_irq_mask; staticunsignedint riscv_pmu_irq;
/* Cache the available counters in a bitmask */ staticunsignedlong cmask;
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH,
0, cmask, 0, edata->event_idx, 0, 0); if (!ret.error) {
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
ret.value, 0x1, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
} elseif (ret.error == SBI_ERR_NOT_SUPPORTED) { /* This event cannot be monitored by any counter */
edata->event_idx = -ENOENT;
}
}
staticvoid pmu_sbi_check_std_events(struct work_struct *work)
{ for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++)
pmu_sbi_check_event(&pmu_hw_event_map[i]);
for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++) for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++) for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++)
pmu_sbi_check_event(&pmu_cache_event_map[i][j][k]);
}
/* * Returns the counter width of a programmable counter and number of hardware * counters. As we don't support heterogeneous CPUs yet, it is okay to just * return the counter width of the first programmable counter.
*/ int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr)
{ int i; union sbi_pmu_ctr_info *info;
u32 hpm_width = 0, hpm_count = 0;
if (!cmask) return -EINVAL;
for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) {
info = &pmu_ctr_list[i]; if (!info) continue; if (!hpm_width && info->csr != CSR_CYCLE && info->csr != CSR_INSTRET)
hpm_width = info->width; if (info->type == SBI_PMU_CTR_TYPE_HW)
hpm_count++;
}
/* * In legacy mode, we have to force the fixed counters for those events * but not in the user access mode as we want to use the other counters * that support sampling/filtering.
*/ if ((hwc->flags & PERF_EVENT_FLAG_LEGACY) && (event->attr.type == PERF_TYPE_HARDWARE)) { if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
cmask = 1;
} elseif (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS) {
cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
cmask = BIT(CSR_INSTRET - CSR_CYCLE);
}
}
/* retrieve the available counter index */ #ifdefined(CONFIG_32BIT)
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
cmask, cflags, hwc->event_base, hwc->config,
hwc->config >> 32); #else
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
cmask, cflags, hwc->event_base, hwc->config, 0); #endif if (ret.error) {
pr_debug("Not able to find a counter for event %lx config %llx\n",
hwc->event_base, hwc->config); return sbi_err_map_linux_errno(ret.error);
}
idx = ret.value; if (!test_bit(idx, &rvpmu->cmask) || !pmu_ctr_list[idx].value) return -ENOENT;
/* Additional sanity check for the counter id */ if (pmu_sbi_ctr_is_fw(idx)) { if (!test_and_set_bit(idx, cpuc->used_fw_ctrs)) return idx;
} else { if (!test_and_set_bit(idx, cpuc->used_hw_ctrs)) return idx;
}
staticint pmu_sbi_event_map(struct perf_event *event, u64 *econfig)
{
u32 type = event->attr.type;
u64 config = event->attr.config; int ret = -ENOENT;
/* * Ensure we are finished checking standard hardware events for * validity before allowing userspace to configure any events.
*/
flush_work(&check_std_events_work);
switch (type) { case PERF_TYPE_HARDWARE: if (config >= PERF_COUNT_HW_MAX) return -EINVAL;
ret = pmu_hw_event_map[event->attr.config].event_idx; break; case PERF_TYPE_HW_CACHE:
ret = pmu_event_find_cache(config); break; case PERF_TYPE_RAW: /* * As per SBI specification, the upper 16 bits must be unused * for a hardware raw event. * Bits 63:62 are used to distinguish between raw events * 00 - Hardware raw event * 10 - SBI firmware events * 11 - Risc-V platform specific firmware event
*/
switch (config >> 62) { case 0: /* Return error any bits [48-63] is set as it is not allowed by the spec */ if (!(config & ~RISCV_PMU_RAW_EVENT_MASK)) {
*econfig = config & RISCV_PMU_RAW_EVENT_MASK;
ret = RISCV_PMU_RAW_EVENT_IDX;
} break; case 2:
ret = (config & 0xFFFF) | (SBI_PMU_EVENT_TYPE_FW << 16); break; case 3: /* * For Risc-V platform specific firmware events * Event code - 0xFFFF * Event data - raw event encoding
*/
ret = SBI_PMU_EVENT_TYPE_FW << 16 | RISCV_PLAT_FW_EVENT;
*econfig = config & RISCV_PMU_PLAT_FW_EVENT_MASK; break; default: break;
} break; default: break;
}
return ret;
}
staticvoid pmu_sbi_snapshot_free(struct riscv_pmu *pmu)
{ int cpu;
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_SNAPSHOT_SET_SHMEM, SBI_SHMEM_DISABLE,
SBI_SHMEM_DISABLE, 0, 0, 0, 0); if (ret.error) {
pr_warn("failed to disable snapshot shared memory\n"); return sbi_err_map_linux_errno(ret.error);
}
return 0;
}
staticint pmu_sbi_snapshot_setup(struct riscv_pmu *pmu, int cpu)
{ struct cpu_hw_events *cpu_hw_evt; struct sbiret ret = {0};
cpu_hw_evt = per_cpu_ptr(pmu->hw_events, cpu); if (!cpu_hw_evt->snapshot_addr_phys) return -EINVAL;
if (cpu_hw_evt->snapshot_set_done) return 0;
if (IS_ENABLED(CONFIG_32BIT))
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_SNAPSHOT_SET_SHMEM,
cpu_hw_evt->snapshot_addr_phys,
(u64)(cpu_hw_evt->snapshot_addr_phys) >> 32, 0, 0, 0, 0); else
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_SNAPSHOT_SET_SHMEM,
cpu_hw_evt->snapshot_addr_phys, 0, 0, 0, 0, 0);
/* Free up the snapshot area memory and fall back to SBI PMU calls without snapshot */ if (ret.error) { if (ret.error != SBI_ERR_NOT_SUPPORTED)
pr_warn("pmu snapshot setup failed with error %ld\n", ret.error); return sbi_err_map_linux_errno(ret.error);
}
/* Read the value from the shared memory directly only if counter is stopped */ if (sbi_pmu_snapshot_available() && (hwc->state & PERF_HES_STOPPED)) {
val = sdata->ctr_values[idx]; return val;
}
if (pmu_sbi_is_fw_event(event)) {
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_FW_READ,
hwc->idx, 0, 0, 0, 0, 0); if (ret.error) return 0;
val = ret.value; if (IS_ENABLED(CONFIG_32BIT) && sbi_v2_available && info.width >= 32) {
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_FW_READ_HI,
hwc->idx, 0, 0, 0, 0, 0); if (!ret.error)
val |= ((u64)ret.value << 32); else
WARN_ONCE(1, "Unable to read upper 32 bits of firmware counter error: %ld\n",
ret.error);
}
} else {
val = riscv_pmu_ctr_read_csr(info.csr); if (IS_ENABLED(CONFIG_32BIT))
val |= ((u64)riscv_pmu_ctr_read_csr(info.csr + 0x80)) << 32;
}
/* There is no benefit setting SNAPSHOT FLAG for a single counter */ #ifdefined(CONFIG_32BIT)
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx,
1, flag, ival, ival >> 32, 0); #else
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx,
1, flag, ival, 0, 0); #endif if (ret.error && (ret.error != SBI_ERR_ALREADY_STARTED))
pr_err("Starting counter idx %d failed with error %d\n",
hwc->idx, sbi_err_map_linux_errno(ret.error));
if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
(hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
pmu_sbi_reset_scounteren((void *)event);
if (sbi_pmu_snapshot_available())
flag |= SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, hwc->idx, 1, flag, 0, 0, 0); if (!ret.error && sbi_pmu_snapshot_available()) { /* * The counter snapshot is based on the index base specified by hwc->idx. * The actual counter value is updated in shared memory at index 0 when counter * mask is 0x01. To ensure accurate counter values, it's necessary to transfer * the counter value to shared memory. However, if hwc->idx is zero, the counter * value is already correctly updated in shared memory, requiring no further * adjustment.
*/ if (hwc->idx > 0) {
sdata->ctr_values[hwc->idx] = sdata->ctr_values[0];
sdata->ctr_values[0] = 0;
}
} elseif (ret.error && (ret.error != SBI_ERR_ALREADY_STOPPED) &&
flag != SBI_PMU_STOP_FLAG_RESET) {
pr_err("Stopping counter idx %d failed with error %d\n",
hwc->idx, sbi_err_map_linux_errno(ret.error));
}
}
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_NUM_COUNTERS, 0, 0, 0, 0, 0, 0); if (!ret.error) return ret.value; else return sbi_err_map_linux_errno(ret.error);
}
staticint pmu_sbi_get_ctrinfo(int nctr, unsignedlong *mask)
{ struct sbiret ret; int i, num_hw_ctr = 0, num_fw_ctr = 0; union sbi_pmu_ctr_info cinfo;
pmu_ctr_list = kcalloc(nctr, sizeof(*pmu_ctr_list), GFP_KERNEL); if (!pmu_ctr_list) return -ENOMEM;
for (i = 0; i < nctr; i++) {
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_GET_INFO, i, 0, 0, 0, 0, 0); if (ret.error) /* The logical counter ids are not expected to be contiguous */ continue;
pr_info("%d firmware and %d hardware counters\n", num_fw_ctr, num_hw_ctr);
return 0;
}
staticinlinevoid pmu_sbi_stop_all(struct riscv_pmu *pmu)
{ /* * No need to check the error because we are disabling all the counters * which may include counters that are not enabled yet.
*/
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
0, pmu->cmask, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
}
staticinlinevoid pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
{ struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events); struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; unsignedlong flag = 0; int i, idx; struct sbiret ret;
u64 temp_ctr_overflow_mask = 0;
if (sbi_pmu_snapshot_available())
flag = SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
/* Reset the shadow copy to avoid save/restore any value from previous overflow */
memset(cpu_hw_evt->snapshot_cval_shcopy, 0, sizeof(u64) * RISCV_MAX_COUNTERS);
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) { /* No need to check the error here as we can't do anything about the error */
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, i * BITS_PER_LONG,
cpu_hw_evt->used_hw_ctrs[i], flag, 0, 0, 0); if (!ret.error && sbi_pmu_snapshot_available()) { /* Save the counter values to avoid clobbering */
for_each_set_bit(idx, &cpu_hw_evt->used_hw_ctrs[i], BITS_PER_LONG)
cpu_hw_evt->snapshot_cval_shcopy[i * BITS_PER_LONG + idx] =
sdata->ctr_values[idx]; /* Save the overflow mask to avoid clobbering */
temp_ctr_overflow_mask |= sdata->ctr_overflow_mask << (i * BITS_PER_LONG);
}
}
/* Restore the counter values to the shared memory for used hw counters */ if (sbi_pmu_snapshot_available()) {
for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS)
sdata->ctr_values[idx] = cpu_hw_evt->snapshot_cval_shcopy[idx]; if (temp_ctr_overflow_mask)
sdata->ctr_overflow_mask = temp_ctr_overflow_mask;
}
}
/* * This function starts all the used counters in two step approach. * Any counter that did not overflow can be start in a single step * while the overflowed counters need to be started with updated initialization * value.
*/ staticinlinevoid pmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
u64 ctr_ovf_mask)
{ int idx = 0, i; struct perf_event *event; unsignedlong flag = SBI_PMU_START_FLAG_SET_INIT_VALUE; unsignedlong ctr_start_mask = 0;
uint64_t max_period; struct hw_perf_event *hwc;
u64 init_val = 0;
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
ctr_start_mask = cpu_hw_evt->used_hw_ctrs[i] & ~ctr_ovf_mask; /* Start all the counters that did not overflow in a single shot */
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, i * BITS_PER_LONG, ctr_start_mask,
0, 0, 0, 0);
}
for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) { if (ctr_ovf_mask & BIT(idx)) {
event = cpu_hw_evt->events[idx];
hwc = &event->hw;
max_period = riscv_pmu_ctr_get_width_mask(event);
init_val = local64_read(&hwc->prev_count) & max_period;
cpu_hw_evt->snapshot_cval_shcopy[idx] = init_val;
} /* * We do not need to update the non-overflow counters the previous * value should have been there already.
*/
}
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) { /* Restore the counter values to relative indices for used hw counters */
for_each_set_bit(idx, &cpu_hw_evt->used_hw_ctrs[i], BITS_PER_LONG)
sdata->ctr_values[idx] =
cpu_hw_evt->snapshot_cval_shcopy[idx + i * BITS_PER_LONG]; /* Start all the counters in a single shot */
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx * BITS_PER_LONG,
cpu_hw_evt->used_hw_ctrs[i], flag, 0, 0, 0);
}
}
/* Overflow status register should only be read after counter are stopped */ if (sbi_pmu_snapshot_available())
overflow = sdata->ctr_overflow_mask; else
ALT_SBI_PMU_OVERFLOW(overflow);
/* * Overflow interrupt pending bit should only be cleared after stopping * all the counters to avoid any race condition.
*/
ALT_SBI_PMU_OVF_CLEAR_PENDING(riscv_pmu_irq_mask);
/* No overflow bit is set */ if (!overflow) return IRQ_NONE;
/* Skip if invalid event or user did not request a sampling */ if (!event || !is_sampling_event(event)) continue;
info = &pmu_ctr_list[lidx]; /* Do a sanity check */ if (!info || info->type != SBI_PMU_CTR_TYPE_HW) continue;
if (sbi_pmu_snapshot_available()) /* SBI implementation already updated the logical indicies */
hidx = lidx; else /* compute hardware counter index */
hidx = info->csr - CSR_CYCLE;
/* check if the corresponding bit is set in sscountovf or overflow mask in shmem */ if (!(overflow & BIT(hidx))) continue;
/* * Keep a track of overflowed counters so that they can be started * with updated initial value.
*/
overflowed_ctrs |= BIT(lidx);
hw_evt = &event->hw; /* Update the event states here so that we know the state while reading */
hw_evt->state |= PERF_HES_STOPPED;
riscv_pmu_event_update(event);
hw_evt->state |= PERF_HES_UPTODATE;
perf_sample_data_init(&data, 0, hw_evt->last_period); if (riscv_pmu_event_set_period(event)) { /* * Unlike other ISAs, RISC-V don't have to disable interrupts * to avoid throttling here. As per the specification, the * interrupt remains disabled until the OF bit is set. * Interrupts are enabled again only during the start. * TODO: We will need to stop the guest counters once * virtualization support is added.
*/
perf_event_overflow(event, &data, regs);
} /* Reset the state as we are going to start the counter after the loop */
hw_evt->state = 0;
}
/* * We keep enabling userspace access to CYCLE, TIME and INSTRET via the * legacy option but that will be removed in the future.
*/ if (sysctl_perf_user_access == SYSCTL_LEGACY)
csr_write(CSR_SCOUNTEREN, 0x7); else
csr_write(CSR_SCOUNTEREN, 0x2);
/* Stop all the counters so that they can be enabled from perf */
pmu_sbi_stop_all(pmu);
if (riscv_pmu_use_irq) {
cpu_hw_evt->irq = riscv_pmu_irq;
ALT_SBI_PMU_OVF_CLEAR_PENDING(riscv_pmu_irq_mask);
enable_percpu_irq(riscv_pmu_irq, IRQ_TYPE_NONE);
}
if (sbi_pmu_snapshot_available()) return pmu_sbi_snapshot_setup(pmu, cpu);
for (idx = 0; idx < RISCV_MAX_COUNTERS; idx++) {
event = cpuc->events[idx]; if (!event) continue;
switch (cmd) { case CPU_PM_ENTER: /* * Stop and update the counter
*/
riscv_pmu_stop(event, PERF_EF_UPDATE); break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED: /* * Restore and enable the counter.
*/
riscv_pmu_start(event, PERF_EF_RELOAD); break; default: break;
}
}
staticvoid riscv_pmu_destroy(struct riscv_pmu *pmu)
{ if (sbi_v2_available) { if (sbi_pmu_snapshot_available()) {
pmu_sbi_snapshot_disable();
pmu_sbi_snapshot_free(pmu);
}
}
riscv_pm_pmu_unregister(pmu);
cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
}
staticvoid pmu_sbi_event_init(struct perf_event *event)
{ /* * The permissions are set at event_init so that we do not depend * on the sysctl value that can change.
*/ if (sysctl_perf_user_access == SYSCTL_NO_USER_ACCESS)
event->hw.flags |= PERF_EVENT_FLAG_NO_USER_ACCESS; elseif (sysctl_perf_user_access == SYSCTL_USER_ACCESS)
event->hw.flags |= PERF_EVENT_FLAG_USER_ACCESS; else
event->hw.flags |= PERF_EVENT_FLAG_LEGACY;
}
if (event->hw.flags & PERF_EVENT_FLAG_LEGACY) { if (event->attr.config != PERF_COUNT_HW_CPU_CYCLES &&
event->attr.config != PERF_COUNT_HW_INSTRUCTIONS) { return;
}
}
/* * The user mmapped the event to directly access it: this is where * we determine based on sysctl_perf_user_access if we grant userspace * the direct access to this event. That means that within the same * task, some events may be directly accessible and some other may not, * if the user changes the value of sysctl_perf_user_accesss in the * meantime.
*/
event->hw.flags |= PERF_EVENT_FLAG_USER_READ_CNT;
/* * We must enable userspace access *before* advertising in the user page * that it is possible to do so to avoid any race. * And we must notify all cpus here because threads that currently run * on other cpus will try to directly access the counter too without * calling pmu_sbi_ctr_start.
*/ if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
on_each_cpu_mask(mm_cpumask(mm),
pmu_sbi_set_scounteren, (void *)event, 1);
}
if (event->hw.flags & PERF_EVENT_FLAG_LEGACY) { if (event->attr.config != PERF_COUNT_HW_CPU_CYCLES &&
event->attr.config != PERF_COUNT_HW_INSTRUCTIONS) { return;
}
}
/* * Here we can directly remove user access since the user does not have * access to the user page anymore so we avoid the racy window where the * user could have read cap_user_rdpmc to true right before we disable * it.
*/
event->hw.flags &= ~PERF_EVENT_FLAG_USER_READ_CNT;
if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
on_each_cpu_mask(mm_cpumask(mm),
pmu_sbi_reset_scounteren, (void *)event, 1);
}
staticint riscv_pmu_proc_user_access_handler(conststruct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{ int prev = sysctl_perf_user_access; int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
/* * Test against the previous value since we clear SCOUNTEREN when * sysctl_perf_user_access is set to SYSCTL_USER_ACCESS, but we should * not do that if that was already the case.
*/ if (ret || !write || prev == sysctl_perf_user_access) return ret;
staticint pmu_sbi_device_probe(struct platform_device *pdev)
{ struct riscv_pmu *pmu = NULL; int ret = -ENODEV; int num_counters;
pr_info("SBI PMU extension is available\n");
pmu = riscv_pmu_alloc(); if (!pmu) return -ENOMEM;
num_counters = pmu_sbi_find_num_ctrs(); if (num_counters < 0) {
pr_err("SBI PMU extension doesn't provide any counters\n"); goto out_free;
}
/* It is possible to get from SBI more than max number of counters */ if (num_counters > RISCV_MAX_COUNTERS) {
num_counters = RISCV_MAX_COUNTERS;
pr_info("SBI returned more than maximum number of counters. Limiting the number of counters to %d\n", num_counters);
}
/* cache all the information about counters now */ if (pmu_sbi_get_ctrinfo(num_counters, &cmask)) goto out_free;
ret = pmu_sbi_setup_irqs(pmu, pdev); if (ret < 0) {
pr_info("Perf sampling/filtering is not supported as sscof extension is not available\n");
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;
}
ret = riscv_pm_pmu_register(pmu); if (ret) goto out_unregister;
ret = perf_pmu_register(&pmu->pmu, "cpu", PERF_TYPE_RAW); if (ret) goto out_unregister;
/* SBI PMU Snapsphot is only available in SBI v2.0 */ if (sbi_v2_available) { int cpu;
ret = pmu_sbi_snapshot_alloc(pmu); if (ret) goto out_unregister;
cpu = get_cpu();
ret = pmu_sbi_snapshot_setup(pmu, cpu);
put_cpu();
if (ret) { /* Snapshot is an optional feature. Continue if not available */
pmu_sbi_snapshot_free(pmu);
} else {
pr_info("SBI PMU snapshot detected\n"); /* * We enable it once here for the boot cpu. If snapshot shmem setup * fails during cpu hotplug process, it will fail to start the cpu * as we can not handle hetergenous PMUs with different snapshot * capability.
*/
static_branch_enable(&sbi_pmu_snapshot_available);
}
}
register_sysctl("kernel", sbi_pmu_sysctl_table);
ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node); if (ret) goto out_unregister;
/* Asynchronously check which standard events are available */
schedule_work(&check_std_events_work);
if (sbi_spec_version >= sbi_mk_version(2, 0))
sbi_v2_available = true;
ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_RISCV_STARTING, "perf/riscv/pmu:starting",
pmu_sbi_starting_cpu, pmu_sbi_dying_cpu); if (ret) {
pr_err("CPU hotplug notifier could not be registered: %d\n",
ret); return ret;
}
ret = platform_driver_register(&pmu_sbi_driver); if (ret) return ret;
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.