Skip to content

Commit

Permalink
RISC-V: PoC for SBI PMU SNAPSHOT
Browse files Browse the repository at this point in the history
Signed-off-by: Atish Patra <atishp@rivosinc.com>
  • Loading branch information
atishp04 committed Jul 29, 2022
1 parent f7c4108 commit 42182a0
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 7 deletions.
3 changes: 3 additions & 0 deletions arch/riscv/include/asm/sbi.h
Expand Up @@ -120,6 +120,7 @@ enum sbi_ext_pmu_fid {
SBI_EXT_PMU_COUNTER_START,
SBI_EXT_PMU_COUNTER_STOP,
SBI_EXT_PMU_COUNTER_FW_READ,
SBI_EXT_PMU_ENABLE_SNAPSHOT,
};

union sbi_pmu_ctr_info {
Expand Down Expand Up @@ -229,9 +230,11 @@ enum sbi_pmu_ctr_type {

/* Flags defined for counter start function */
#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
#define SBI_PMU_START_FLAG_INIT_FROM_SNAPSHOT (1 << 1)

/* Flags defined for counter stop function */
#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
#define SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT (1 << 1)

#define SBI_SPEC_VERSION_DEFAULT 0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
Expand Down
1 change: 1 addition & 0 deletions arch/riscv/kernel/vdso.c
Expand Up @@ -14,6 +14,7 @@
#include <asm/page.h>
#include <asm/vdso.h>
#include <linux/time_namespace.h>
#include <asm/vdso/vsyscall.h>

#ifdef CONFIG_GENERIC_TIME_VSYSCALL
#include <vdso/datapage.h>
Expand Down
98 changes: 91 additions & 7 deletions drivers/perf/riscv_pmu_sbi.c
Expand Up @@ -435,19 +435,28 @@ static u64 pmu_sbi_ctr_read(struct perf_event *event)
struct sbiret ret;
union sbi_pmu_ctr_info info;
u64 val = 0;
struct riscv_pmu *pmu = to_riscv_pmu(event->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;

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)
val = ret.value;
} else {
if (sdata) {
/* Read the value from the shared memory */
val = sdata->hpmcounter[idx];
goto done;
}
info = pmu_ctr_list[idx];
val = riscv_pmu_ctr_read_csr(info.csr);
if (IS_ENABLED(CONFIG_32BIT))
val = ((u64)riscv_pmu_ctr_read_csr(info.csr + 0x80)) << 31 | val;
}

done:
return val;
}

Expand All @@ -457,6 +466,7 @@ static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival)
struct hw_perf_event *hwc = &event->hw;
unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE;

/* There is no benefit setting SNAPSHOT FLAG for a single counter */
#if defined(CONFIG_32BIT)
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx,
1, flag, ival, ival >> 32, 0);
Expand All @@ -473,6 +483,11 @@ static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
{
struct sbiret ret;
struct hw_perf_event *hwc = &event->hw;
struct riscv_pmu *pmu = to_riscv_pmu(event->pmu);
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);

if (cpu_hw_evt->snapshot_addr)
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 && (ret.error != SBI_ERR_ALREADY_STOPPED) &&
Expand Down Expand Up @@ -534,10 +549,14 @@ static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
{
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
unsigned long flag = 0;

if (cpu_hw_evt->snapshot_addr)
flag = SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;

/* No need to check the error here as we can't do anything about the error */
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, 0,
cpu_hw_evt->used_hw_ctrs[0], 0, 0, 0, 0);
cpu_hw_evt->used_hw_ctrs[0], flag, 0, 0, 0);
}

/*
Expand All @@ -546,11 +565,10 @@ static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
* while the overflowed counters need to be started with updated initialization
* value.
*/
static inline void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
unsigned long ctr_ovf_mask)
static noinline void pmu_sbi_start_overflow_mask_sbi(struct cpu_hw_events *cpu_hw_evt,
unsigned long ctr_ovf_mask)
{
int idx = 0;
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
struct perf_event *event;
unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE;
unsigned long ctr_start_mask = 0;
Expand All @@ -559,7 +577,6 @@ static inline void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
u64 init_val = 0;

ctr_start_mask = cpu_hw_evt->used_hw_ctrs[0] & ~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, 0, ctr_start_mask,
0, 0, 0, 0);
Expand All @@ -585,6 +602,44 @@ static inline void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
}
}

static void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
unsigned long ctr_ovf_mask)
{
int idx = 0;
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
struct perf_event *event;
unsigned long flag = SBI_PMU_START_FLAG_INIT_FROM_SNAPSHOT;
uint64_t max_period;
struct hw_perf_event *hwc;
u64 init_val = 0;
unsigned long ctr_start_mask = 0;
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;

if (!sdata) {
pmu_sbi_start_overflow_mask_sbi(cpu_hw_evt, ctr_ovf_mask);
return;
}

for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) {
if (ctr_ovf_mask & (1 << 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;
sdata->hpmcounter[idx] = init_val;
//TODO: Do we need to the bit in scountovf
}
/* We donot need to update the non-overflow counters the previous
* value should have been there already.
*/
}

ctr_start_mask = cpu_hw_evt->used_hw_ctrs[0];
/* Start all the counters in a single shot */
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, 0, ctr_start_mask,
flag, 0, 0, 0);
}

static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
{
struct perf_sample_data data;
Expand All @@ -597,6 +652,7 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
unsigned long overflow;
unsigned long overflowed_ctrs = 0;
struct cpu_hw_events *cpu_hw_evt = dev;
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;

if (WARN_ON_ONCE(!cpu_hw_evt))
return IRQ_NONE;
Expand All @@ -613,8 +669,10 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
pmu_sbi_stop_hw_ctrs(pmu);

/* Overflow status register should only be read after counter are stopped */
overflow = csr_read(CSR_SSCOUNTOVF);

if (!sdata)
overflow = csr_read(CSR_SSCOUNTOVF);
else
overflow = sdata->scountovf;
/*
* Overflow interrupt pending bit should only be cleared after stopping
* all the counters to avoid any race condition.
Expand Down Expand Up @@ -674,6 +732,8 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
{
struct riscv_pmu *pmu = hlist_entry_safe(node, struct riscv_pmu, node);
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
struct sbiret ret;
unsigned long saddr_phys;

/* Enable the access for TIME csr only from the user mode now */
csr_write(CSR_SCOUNTEREN, 0x2);
Expand All @@ -688,6 +748,30 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
enable_percpu_irq(riscv_pmu_irq, IRQ_TYPE_NONE);
}

//TODO: This should only happen if the SBI version is > 2.0

/* Enable snapshot optimization if available */
cpu_hw_evt->snapshot_addr = kmalloc(sizeof(struct riscv_pmu_snapshot_data),
GFP_ATOMIC);
if (!cpu_hw_evt->snapshot_addr) {
pr_warn("Couldn't allocate memory for snapshot. Falling back to SBI\n");
return 0;
}

memset(cpu_hw_evt->snapshot_addr, 0, sizeof(struct riscv_pmu_snapshot_data));
//TODO: Update version

saddr_phys = __pa((unsigned long)(cpu_hw_evt->snapshot_addr));
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_ENABLE_SNAPSHOT, saddr_phys,
0, 0, 0, 0, 0);
/* This will allow to fallback to SBI calls if ENABLE_SNAPSHOT is
* not implemented by the SBI implementation.
*/
//TODO: Should we do a version mismatch here ?

if (ret.error)
cpu_hw_evt->snapshot_addr = NULL;

return 0;
}

Expand Down
9 changes: 9 additions & 0 deletions include/linux/perf/riscv_pmu.h
Expand Up @@ -28,6 +28,13 @@

#define RISCV_KVM_PMU_CONFIG1_GUEST_EVENTS 0x1

/* Data structure to contain the snapshot data */
struct riscv_pmu_snapshot_data {
uint64_t hpmcounter[32];
uint32_t scountovf;
uint32_t version;
};

struct cpu_hw_events {
/* currently enabled events */
int n_events;
Expand All @@ -39,6 +46,8 @@ struct cpu_hw_events {
DECLARE_BITMAP(used_hw_ctrs, RISCV_MAX_COUNTERS);
/* currently enabled firmware counters */
DECLARE_BITMAP(used_fw_ctrs, RISCV_MAX_COUNTERS);
/* The virtual address of the shared memory where counter snapshot will be taken */
void *snapshot_addr;
};

struct riscv_pmu {
Expand Down

0 comments on commit 42182a0

Please sign in to comment.