Received: by 10.213.65.68 with SMTP id h4csp2151604imn; Thu, 5 Apr 2018 09:49:18 -0700 (PDT) X-Google-Smtp-Source: AIpwx4+TFMLmULXXws1ObtWXD5B36q3AAkamUuBZl8JYt4ScpvyADKiC+EzFJSSa3Wu4VcyogjfI X-Received: by 2002:a17:902:ab86:: with SMTP id f6-v6mr24375683plr.258.1522946958612; Thu, 05 Apr 2018 09:49:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1522946958; cv=none; d=google.com; s=arc-20160816; b=isE+eqBNocowr+/eBlp0DfOjfBb2dADMXwVG9uXOpziXKg6YGfQAPA/B4+R56t4BwK QETClKJ+f62ZYX6yYMDm6Y2Lhiyn7IM23ewHTCzCAi8h/T++kVJKwif1J533ZF96l/vn bFULhV0+kpLcZ7DBQos+YAAnuttjIUdwIsts6XrTAKhH6QC+gAEzt0HeMazRndb9X0kb wkWsV4XJK9M+5Lr8IxAgnDjnLJwO3LFwAATjY5pbEhWVOr755+KxvbmvPm1IfplezYwR jMnf1sA7sblkHD0ja6T3NLvxyECblWnDBRs1OqGcju6o2GEbE3jRdtUukqPkt+HhQ62o 4QcQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:to:from:cc:in-reply-to:subject:date:dkim-signature :arc-authentication-results; bh=tH8uUVaoO7qcddgS3+iJtNB+e4N9gK0W9/7xeXg3n04=; b=R4buAofmw3uKBNCzyf4xC6jdRKmy3G8iYX6U1BnirJk2YUK6U5XUUfHza2tFT+AKcp jM1lWitZhxWrz521BQ0KQ8sYx0m+I0cQPBgSdNkdy2gSj7JrXtOohBIms3L7L6qr+sc9 Ub5HETKQdHiSaVF2TnwoW/pxWzOten5ZtwJrMSyJizUhft4ci07XgXsev7SLYJJgamH0 jbW95n2DBqYFaWIiYfC90shUGyp3Se0U8lhARQl1m0yxaneB0SpQzLugCvfFmPqMMYtx FpZSHCB+mCsZIF8TvqkM5RREOki3Q23+IKM0DZgUxEkE6g48UYZqF6KnabnLiashtUGA A8qw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@sifive.com header.s=google header.b=W1beeMhn; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p19-v6si6364856plo.708.2018.04.05.09.49.04; Thu, 05 Apr 2018 09:49:18 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@sifive.com header.s=google header.b=W1beeMhn; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751344AbeDEQry (ORCPT + 99 others); Thu, 5 Apr 2018 12:47:54 -0400 Received: from mail-pl0-f67.google.com ([209.85.160.67]:39140 "EHLO mail-pl0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750726AbeDEQrw (ORCPT ); Thu, 5 Apr 2018 12:47:52 -0400 Received: by mail-pl0-f67.google.com with SMTP id s24-v6so19938956plq.6 for ; Thu, 05 Apr 2018 09:47:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; h=date:subject:in-reply-to:cc:from:to:message-id:mime-version :content-transfer-encoding; bh=tH8uUVaoO7qcddgS3+iJtNB+e4N9gK0W9/7xeXg3n04=; b=W1beeMhnWlPo8xE6nBjFs/iunBDbjCs9BoUmrvQXJFlOUW/yKDXgQTSbUU0Edr3YPM AO4pzRsKyRDFyEeyn33nDT7608Loh2ym/zbyyrAewBZr3qLFy/dLsIhsnn+Ow46eVgZK qiYc5AG8OJR4UIrfK4WBgau9A64lVlCJKhL9oHltvyzBOYNEEv0RIZsUewnRpKdhX+oT 5zMkiWXuJn/Q0KDUQZX8yQVGoLiTgzrjLg9QcwdomZpSmNOGwyprhIwu65AOPModxCZs 9lE3iMLUdYRTRX5aA4Lsju7z4Y6l06br2iKvcaTuBIHTAnU31UZWbFtrxt4PTqTcZLn/ 9xDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:subject:in-reply-to:cc:from:to:message-id :mime-version:content-transfer-encoding; bh=tH8uUVaoO7qcddgS3+iJtNB+e4N9gK0W9/7xeXg3n04=; b=ZpAdRthPPvqywxuBemhTL2hrDjyjsZZmzCNQTTii8Ziej4t8UmW0oAGO7Fjk2Cnlra SR7baJvWcfcij9U4EOeAi9F1Npq8vP+r7323LS9wTXMCRrVnVTI84ip9MexnXmTEnGzn zuo751PSmPVPfFdvgLPj/SZrNZHIR1dttLewdJbRRs90/dFEWuG+lsthPbMykkQ8ybV3 Z/HR1Ui2oihaO4qv7bJZwn5ffnkC/5RPsxysE/1Hh04ihOtq6WWNrOazGZN7xAjyQu0b iNzIGnInxkjwRXUUwLVDxh6o4CnuRrqkkJVYMjqhuc0ZUeOpp5dF9VuO+S6DX3GmFV7N CPag== X-Gm-Message-State: AElRT7HNweHAYkE3U0LXHmO6Y7vMBfzTXLXPqeHcYRRJjxXE94Xb5svB Q1qDoskSb4/hDZrFRLvuFDMcYg== X-Received: by 2002:a17:902:820a:: with SMTP id x10-v6mr14350374pln.306.1522946871487; Thu, 05 Apr 2018 09:47:51 -0700 (PDT) Received: from localhost ([12.206.222.5]) by smtp.gmail.com with ESMTPSA id l90sm18319298pfb.96.2018.04.05.09.47.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 05 Apr 2018 09:47:50 -0700 (PDT) Date: Thu, 05 Apr 2018 09:47:50 -0700 (PDT) X-Google-Original-Date: Thu, 05 Apr 2018 09:45:54 PDT (-0700) Subject: Re: [PATCH 1/2] perf: riscv: preliminary RISC-V support In-Reply-To: <1522051075-6442-2-git-send-email-alankao@andestech.com> CC: albert@sifive.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, alexander.shishkin@linux.intel.com, jolsa@redhat.com, namhyung@kernel.org, sols@sifive.com, corbet@lwn.net, linux-riscv@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, alankao@andestech.com, nickhu@andestech.com, greentime@andestech.com From: Palmer Dabbelt To: alankao@andestech.com Message-ID: Mime-Version: 1.0 (MHng) Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, 26 Mar 2018 00:57:54 PDT (-0700), alankao@andestech.com wrote: > This patch provide a basic PMU, riscv_base_pmu, which supports two > general hardware event, instructions and cycles. Furthermore, this > PMU serves as a reference implementation to ease the portings in > the future. > > riscv_base_pmu should be able to run on any RISC-V machine that > conforms to the Priv-Spec. Note that the latest qemu model hasn't > fully support a proper behavior of Priv-Spec 1.10 yet, but work > around should be easy with very small fixes. Please check > https://github.com/riscv/riscv-qemu/pull/115 for future updates. > > Cc: Nick Hu > Cc: Greentime Hu > Signed-off-by: Alan Kao We should really be able to detect PMU types at runtime (via a device tree entry) rather than requiring that a single PMU is built in to the kernel. This will require a handful of modifications to how this patch works, which I'll try to list below. > --- > arch/riscv/Kconfig | 12 + > arch/riscv/include/asm/perf_event.h | 76 +++++- > arch/riscv/kernel/Makefile | 1 + > arch/riscv/kernel/perf_event.c | 469 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 554 insertions(+), 4 deletions(-) > create mode 100644 arch/riscv/kernel/perf_event.c > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > index 310b9a5d6737..dd4aecfb5265 100644 > --- a/arch/riscv/Kconfig > +++ b/arch/riscv/Kconfig > @@ -195,6 +195,18 @@ config RISCV_ISA_C > config RISCV_ISA_A > def_bool y > > +menu "PMU type" > + depends on PERF_EVENTS > + > +config RISCV_BASE_PMU > + bool "Base Performance Monitoring Unit" > + def_bool y > + help > + A base PMU that serves as a reference implementation and has limited > + feature of perf. > + > +endmenu > + > endmenu Rather than a menu where a single PMU can be selected, there should be options to enable or disable support for each PMU type -- this is just like how all our other drivers work. > menu "Kernel type" > diff --git a/arch/riscv/include/asm/perf_event.h b/arch/riscv/include/asm/perf_event.h > index e13d2ff29e83..98e2efb02d25 100644 > --- a/arch/riscv/include/asm/perf_event.h > +++ b/arch/riscv/include/asm/perf_event.h > @@ -1,13 +1,81 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > /* > * Copyright (C) 2018 SiFive > + * Copyright (C) 2018 Andes Technology Corporation > * > - * This program is free software; you can redistribute it and/or > - * modify it under the terms of the GNU General Public Licence > - * as published by the Free Software Foundation; either version > - * 2 of the Licence, or (at your option) any later version. > */ > > #ifndef _ASM_RISCV_PERF_EVENT_H > #define _ASM_RISCV_PERF_EVENT_H > > +#include > +#include > + > +#define RISCV_BASE_COUNTERS 2 > + > +/* > + * The RISCV_MAX_COUNTERS parameter should be specified. > + */ > + > +#ifdef CONFIG_RISCV_BASE_PMU > +#define RISCV_MAX_COUNTERS 2 > +#endif > + > +#ifndef RISCV_MAX_COUNTERS > +#error "Please provide a valid RISCV_MAX_COUNTERS for the PMU." > +#endif > + > +/* > + * These are the indexes of bits in counteren register *minus* 1, > + * except for cycle. It would be coherent if it can directly mapped > + * to counteren bit definition, but there is a *time* register at > + * counteren[1]. Per-cpu structure is scarce resource here. > + * > + * According to the spec, an implementation can support counter up to > + * mhpmcounter31, but many high-end processors has at most 6 general > + * PMCs, we give the definition to MHPMCOUNTER8 here. > + */ > +#define RISCV_PMU_CYCLE 0 > +#define RISCV_PMU_INSTRET 1 > +#define RISCV_PMU_MHPMCOUNTER3 2 > +#define RISCV_PMU_MHPMCOUNTER4 3 > +#define RISCV_PMU_MHPMCOUNTER5 4 > +#define RISCV_PMU_MHPMCOUNTER6 5 > +#define RISCV_PMU_MHPMCOUNTER7 6 > +#define RISCV_PMU_MHPMCOUNTER8 7 > + > +#define RISCV_OP_UNSUPP (-EOPNOTSUPP) > + > +struct cpu_hw_events { > + /* # currently enabled events*/ > + int n_events; > + /* currently enabled events */ > + struct perf_event *events[RISCV_MAX_COUNTERS]; > + /* vendor-defined PMU data */ > + void *platform; > +}; > + > +struct riscv_pmu { > + struct pmu *pmu; > + > + /* generic hw/cache events table */ > + const int *hw_events; > + const int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] > + [PERF_COUNT_HW_CACHE_OP_MAX] > + [PERF_COUNT_HW_CACHE_RESULT_MAX]; > + /* method used to map hw/cache events */ > + int (*map_hw_event)(u64 config); > + int (*map_cache_event)(u64 config); > + > + /* max generic hw events in map */ > + int max_events; > + /* number total counters, 2(base) + x(general) */ > + int num_counters; > + /* the width of the counter */ > + int counter_width; > + > + /* vendor-defined PMU features */ > + void *platform; > +}; > + > #endif /* _ASM_RISCV_PERF_EVENT_H */ > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile > index 196f62ffc428..849c38d9105f 100644 > --- a/arch/riscv/kernel/Makefile > +++ b/arch/riscv/kernel/Makefile > @@ -36,5 +36,6 @@ obj-$(CONFIG_SMP) += smp.o > obj-$(CONFIG_MODULES) += module.o > obj-$(CONFIG_FUNCTION_TRACER) += mcount.o > obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o > +obj-$(CONFIG_PERF_EVENTS) += perf_event.o > > clean: > diff --git a/arch/riscv/kernel/perf_event.c b/arch/riscv/kernel/perf_event.c > new file mode 100644 > index 000000000000..b78cb486683b > --- /dev/null > +++ b/arch/riscv/kernel/perf_event.c > @@ -0,0 +1,469 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2008 Thomas Gleixner > + * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar > + * Copyright (C) 2009 Jaswinder Singh Rajput > + * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter > + * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra > + * Copyright (C) 2009 Intel Corporation, > + * Copyright (C) 2009 Google, Inc., Stephane Eranian > + * Copyright 2014 Tilera Corporation. All Rights Reserved. > + * Copyright (C) 2018 Andes Technology Corporation > + * > + * Perf_events support for RISC-V platforms. > + * > + * Since the spec. (as of now, Priv-Spec 1.10) does not provide enough > + * functionality for perf event to fully work, this file provides > + * the very basic framework only. > + * > + * For platform portings, please check Documentations/riscv/pmu.txt. > + * > + * The Copyright line includes x86 and tile ones. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static const struct riscv_pmu *riscv_pmu __read_mostly; > +static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); > + > +/* > + * Hardware & cache maps and their methods > + */ > + > +static const int riscv_hw_event_map[] = { > + [PERF_COUNT_HW_CPU_CYCLES] = RISCV_PMU_CYCLE, > + [PERF_COUNT_HW_INSTRUCTIONS] = RISCV_PMU_INSTRET, > + [PERF_COUNT_HW_CACHE_REFERENCES] = RISCV_OP_UNSUPP, > + [PERF_COUNT_HW_CACHE_MISSES] = RISCV_OP_UNSUPP, > + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = RISCV_OP_UNSUPP, > + [PERF_COUNT_HW_BRANCH_MISSES] = RISCV_OP_UNSUPP, > + [PERF_COUNT_HW_BUS_CYCLES] = RISCV_OP_UNSUPP, > +}; > + > +#define C(x) PERF_COUNT_HW_CACHE_##x > +static const int riscv_cache_event_map[PERF_COUNT_HW_CACHE_MAX] > +[PERF_COUNT_HW_CACHE_OP_MAX] > +[PERF_COUNT_HW_CACHE_RESULT_MAX] = { > + [C(L1D)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > + [C(L1I)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > + [C(LL)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > + [C(DTLB)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > + [C(ITLB)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > + [C(BPU)] = { > + [C(OP_READ)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_WRITE)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + [C(OP_PREFETCH)] = { > + [C(RESULT_ACCESS)] = RISCV_OP_UNSUPP, > + [C(RESULT_MISS)] = RISCV_OP_UNSUPP, > + }, > + }, > +}; > + > +static int riscv_map_hw_event(u64 config) > +{ > + if (config >= riscv_pmu->max_events) > + return -EINVAL; > + > + return riscv_pmu->hw_events[config]; > +} > + > +int riscv_map_cache_decode(u64 config, unsigned int *type, > + unsigned int *op, unsigned int *result) > +{ > + return -ENOENT; > +} > + > +static int riscv_map_cache_event(u64 config) > +{ > + unsigned int type, op, result; > + int err = -ENOENT; > + int code; > + > + err = riscv_map_cache_decode(config, &type, &op, &result); > + if (!riscv_pmu->cache_events || err) > + return err; > + > + if (type >= PERF_COUNT_HW_CACHE_MAX || > + op >= PERF_COUNT_HW_CACHE_OP_MAX || > + result >= PERF_COUNT_HW_CACHE_RESULT_MAX) > + return -EINVAL; > + > + code = (*riscv_pmu->cache_events)[type][op][result]; > + if (code == RISCV_OP_UNSUPP) > + return -EINVAL; > + > + return code; > +} > + > +/* > + * Low-level functions: reading/writing counters > + */ > + > +static inline u64 read_counter(int idx) > +{ > + u64 val = 0; > + > + switch (idx) { > + case RISCV_PMU_CYCLE: > + val = csr_read(cycle); > + break; > + case RISCV_PMU_INSTRET: > + val = csr_read(instret); > + break; > + default: > + WARN_ON_ONCE(idx < 0 || idx > RISCV_MAX_COUNTERS); > + return -EINVAL; > + } > + > + return val; > +} > + > +static inline void write_counter(int idx, u64 value) > +{ > + /* currently not supported */ > +} > + > +/* > + * pmu->read: read and update the counter > + * > + * Other architectures' implementation often have a xxx_perf_event_update > + * routine, which can return counter values when called in the IRQ, but > + * return void when being called by the pmu->read method. > + */ > +static void riscv_pmu_read(struct perf_event *event) > +{ > + struct hw_perf_event *hwc = &event->hw; > + u64 prev_raw_count, new_raw_count; > + u64 oldval; > + int idx = hwc->idx; > + u64 delta; > + > + do { > + prev_raw_count = local64_read(&hwc->prev_count); > + new_raw_count = read_counter(idx); > + > + oldval = local64_cmpxchg(&hwc->prev_count, prev_raw_count, > + new_raw_count); > + } while (oldval != prev_raw_count); > + > + /* > + * delta is the value to update the counter we maintain in the kernel. > + */ > + delta = (new_raw_count - prev_raw_count) & > + ((1ULL << riscv_pmu->counter_width) - 1); > + local64_add(delta, &event->count); > + /* > + * Something like local64_sub(delta, &hwc->period_left) here is > + * needed if there is an interrupt for perf. > + */ > +} > + > +/* > + * State transition functions: > + * > + * stop()/start() & add()/del() > + */ > + > +/* > + * pmu->stop: stop the counter > + */ > +static void riscv_pmu_stop(struct perf_event *event, int flags) > +{ > + struct hw_perf_event *hwc = &event->hw; > + > + WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); > + hwc->state |= PERF_HES_STOPPED; > + > + if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) { > + riscv_pmu_read(event); > + hwc->state |= PERF_HES_UPTODATE; > + } > +} > + > +/* > + * pmu->start: start the event. > + */ > +static void riscv_pmu_start(struct perf_event *event, int flags) > +{ > + struct hw_perf_event *hwc = &event->hw; > + > + if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) > + return; > + > + if (flags & PERF_EF_RELOAD) { > + WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); > + > + /* > + * Set the counter to the period to the next interrupt here, > + * if you have any. > + */ > + } > + > + hwc->state = 0; > + perf_event_update_userpage(event); > + > + /* > + * Since we cannot write to counters, this serves as an initialization > + * to the delta-mechanism in pmu->read(); otherwise, the delta would be > + * wrong when pmu->read is called for the first time. > + */ > + if (local64_read(&hwc->prev_count) == 0) > + local64_set(&hwc->prev_count, read_counter(hwc->idx)); > +} > + > +/* > + * pmu->add: add the event to PMU. > + */ > +static int riscv_pmu_add(struct perf_event *event, int flags) > +{ > + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); > + struct hw_perf_event *hwc = &event->hw; > + > + if (cpuc->n_events == riscv_pmu->num_counters) > + return -ENOSPC; > + > + /* > + * We don't have general conunters, so no binding-event-to-counter > + * process here. > + * > + * Indexing using hwc->config generally not works, since config may > + * contain extra information, but here the only info we have in > + * hwc->config is the event index. > + */ > + hwc->idx = hwc->config; > + cpuc->events[hwc->idx] = event; > + cpuc->n_events++; > + > + hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; > + > + if (flags & PERF_EF_START) > + riscv_pmu_start(event, PERF_EF_RELOAD); > + > + return 0; > +} > + > +/* > + * pmu->del: delete the event from PMU. > + */ > +static void riscv_pmu_del(struct perf_event *event, int flags) > +{ > + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); > + struct hw_perf_event *hwc = &event->hw; > + > + cpuc->events[hwc->idx] = NULL; > + cpuc->n_events--; > + riscv_pmu_stop(event, PERF_EF_UPDATE); > + perf_event_update_userpage(event); > +} > + > +/* > + * Interrupt > + */ > + > +static DEFINE_MUTEX(pmc_reserve_mutex); > +typedef void (*perf_irq_t)(void *riscv_perf_irq); > +perf_irq_t perf_irq; > + > +void riscv_pmu_handle_irq(void *riscv_perf_irq) > +{ > +} > + > +static perf_irq_t reserve_pmc_hardware(void) > +{ > + perf_irq_t old; > + > + mutex_lock(&pmc_reserve_mutex); > + old = perf_irq; > + perf_irq = &riscv_pmu_handle_irq; > + mutex_unlock(&pmc_reserve_mutex); > + > + return old; > +} > + > +void release_pmc_hardware(void) > +{ > + mutex_lock(&pmc_reserve_mutex); > + perf_irq = NULL; > + mutex_unlock(&pmc_reserve_mutex); > +} > + > +/* > + * Event Initialization > + */ > + > +static atomic_t riscv_active_events; > + > +static void riscv_event_destroy(struct perf_event *event) > +{ > + if (atomic_dec_return(&riscv_active_events) == 0) > + release_pmc_hardware(); > +} > + > +static int riscv_event_init(struct perf_event *event) > +{ > + struct perf_event_attr *attr = &event->attr; > + struct hw_perf_event *hwc = &event->hw; > + perf_irq_t old_irq_handler = NULL; > + int code; > + > + if (atomic_inc_return(&riscv_active_events) == 1) > + old_irq_handler = reserve_pmc_hardware(); > + > + if (old_irq_handler) { > + pr_warn("PMC hardware busy (reserved by oprofile)\n"); > + atomic_dec(&riscv_active_events); > + return -EBUSY; > + } > + > + switch (event->attr.type) { > + case PERF_TYPE_HARDWARE: > + code = riscv_pmu->map_hw_event(attr->config); > + break; > + case PERF_TYPE_HW_CACHE: > + code = riscv_pmu->map_cache_event(attr->config); > + break; > + case PERF_TYPE_RAW: > + return -EOPNOTSUPP; > + default: > + return -ENOENT; > + } > + > + event->destroy = riscv_event_destroy; > + if (code < 0) { > + event->destroy(event); > + return code; > + } > + > + /* > + * idx is set to -1 because the index of a general event should not be > + * decided until binding to some counter in pmu->add(). > + * > + * But since we don't have such support, later in pmu->add(), we just > + * use hwc->config as the index instead. > + */ > + hwc->config = code; > + hwc->idx = -1; > + > + return 0; > +} > + > +/* > + * Initialization > + */ > + > +static struct pmu min_pmu = { > + .name = "riscv-base", > + .event_init = riscv_event_init, > + .add = riscv_pmu_add, > + .del = riscv_pmu_del, > + .start = riscv_pmu_start, > + .stop = riscv_pmu_stop, > + .read = riscv_pmu_read, > +}; > + > +static const struct riscv_pmu riscv_base_pmu = { > + .pmu = &min_pmu, > + .max_events = ARRAY_SIZE(riscv_hw_event_map), > + .map_hw_event = riscv_map_hw_event, > + .hw_events = riscv_hw_event_map, > + .map_cache_event = riscv_map_cache_event, > + .cache_events = &riscv_cache_event_map, > + .counter_width = 63, > + .num_counters = RISCV_BASE_COUNTERS + 0, > +}; > + > +struct pmu * __weak __init riscv_init_platform_pmu(void) > +{ > + riscv_pmu = &riscv_base_pmu; > + return riscv_pmu->pmu; > +} Rather than relying on a weak symbol that gets overridden by other PMU types, this should look through the device tree for a compatible PMU (in the case of just the base PMU it could be any RISC-V hart) and install a PMU handler for it. There'd probably be some sort of priority scheme here, like there are for other driver subsystems, where we'd pick the best PMU driver that's compatible with the PMUs on every hart. > + > +int __init init_hw_perf_events(void) > +{ > + struct pmu *pmu = riscv_init_platform_pmu(); > + > + perf_irq = NULL; > + perf_pmu_register(pmu, "cpu", PERF_TYPE_RAW); > + return 0; > +} > +arch_initcall(init_hw_perf_events); Since we only have a single PMU type right now this isn't critical to handle right away, but we will have to refactor this before adding another PMU.