From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id F0E60CCFA03 for ; Sat, 1 Nov 2025 15:44:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=7q2ByEqhMc9vyjpvRIYTnNHGcdwqGcdLfeyQHqMapc8=; b=NYq3FXHN2Y0xHP qTFFSEdM0ImfP/T/JAWAJ9wJipLbIVse+MG+doP1DJtHrLZIklxLYk1ls03lHahaQNYD72ay9Xl7c AIIluh9y9XAds0uTXlgv34cLD2WPyIFq4SQZa/6TD7C8RhdLJFHzV9+aDdNLRAOC2ihgyVJbW5odg C74+g9N1qwq7tO2Br22WkGMnidsvItG7/Hli4q/CpUGyBteP0CrKaYvfnaZj4SOEeXRwZskhBcJNW jXlEjbvbIB6GRdJnpAYm3vwoxGh2OTZn7tg1KXbA+rYF526MNLxKbHqGO2d8TJMoKJn2UTirpylZ4 16iENQHAy/oF2pJMaULg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1vFDm8-00000007tFS-0vTz; Sat, 01 Nov 2025 15:44:20 +0000 Received: from mail-pj1-x102e.google.com ([2607:f8b0:4864:20::102e]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1vFDm5-00000007tDv-3hQN for linux-riscv@lists.infradead.org; Sat, 01 Nov 2025 15:44:19 +0000 Received: by mail-pj1-x102e.google.com with SMTP id 98e67ed59e1d1-330b4739538so3155572a91.3 for ; Sat, 01 Nov 2025 08:44:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ventanamicro.com; s=google; t=1762011857; x=1762616657; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uw3xX0lWv0Z5caNa0HF905Jm6Ibav/ckH29ZGZIeYG0=; b=mZ0uhr2AJsREOxHyaC55YPJ/DEQMq3Fr7iSknBJEga5tOKwjdZO7AQLPhCfKIvT+lf MdRB4bgXcig+va67yt/y8Q/AvHJqqRatBUvlxYweR3uCvA9tB2Stmlbbgn4w79ZzC4Mc OI/zaHbJSqf3k6cwgSuvygE/BM9yabHC/j/Q5bUUiTpYRGySsze0vAUrcsa+MUlMGuI8 uaqrWP6EyJ9AroQGj4BwuO0m6FJpG6qMLaN6fXwzP5L/FEYD9TKpOS4vH+cDQKh7CrDx PTXxjExCw494x/ct8ZKqT87CRtJT3M24wHdSyYsrBUkvbTdGu5tLI/AbnxRYlNOpkfrc e8ZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762011857; x=1762616657; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uw3xX0lWv0Z5caNa0HF905Jm6Ibav/ckH29ZGZIeYG0=; b=LILlknswhHsCDogqs6dYq+qfnK767BsjyO9g/6CWODMzwKFT4CGSYkV+u6KO81fqZ2 TougSrbVCD8fSt3sKsVuUvXk7rCMnKtLvnTdXv48VKlKTUsRjYm5uWwV52PlTVxewXf2 o6VAfzXnDs2fgOpW7jhYOdLyGZLCuF+fg6FymQjxSr8a01Ugs07KFCyBG9oBU9aUv8r6 aV4frvMziJ9/q2ClNJLx5/uYKd1htR4zntpBM5UHSMBMf80gKfRT2aoe3y1WuTo8cKqK MqZp8S3MbtZpEuot7hHosct9dwYmAc7YhCtAxsDkWFcRalvbU8HLXVfADaYyDs6DbKPB D6Rg== X-Forwarded-Encrypted: i=1; AJvYcCUMrZNITl4U/Pc+m60me4A1HAU1MX/n6F2V5JMr99A1SrPTGfYc1qiUwn9+I1hpRYxXsKoQxrO3cOWqsA==@lists.infradead.org X-Gm-Message-State: AOJu0YwDBtcRvcq5lw9E6d2LnfhMZwrni6HboPwkyNnGswBq7cM76XyY nZRnTJImgmwDaQN1e/oqpx+zfwSFngA0Z99HXmZvi8UvdVYXnNXtHOCQaUrK5mvSWno= X-Gm-Gg: ASbGncvciuxpbIZz0lPOsY3POFkW2xwezu3VwnfkzSO4vuArW2es89Mtvl25tupdaOc fUsyE7/ATb3Jt2VWoBN1Dp6vVYZ/EOt5VoGKpR8Qal2D8pGnEIwKkgKtHReSCXOTRpPObohnquS ISkkO6PR5FtDLKtQtW+C5nilNuS+LFZfwZ/5xexmuVIXtrPS2w22h+2yS53StCXHH7R95A5WZZy mycg9hzLfojvjSYsgezSyXVdbqYososEmx7u3PxdoNjbtLscro5V69w0eoweWTh9GotvA8CCrmc vLgf+rDCvI9nsW2srxDtrbGF21Kcsh1Gid+xgYw2aoAsAUdfWcwIc79aGgqAGF9fNDstGEAvf2b JcLAY6S4uOhA6Os0foNV76j2ZOA46t8UHnoNMgr+RzXNgYVJ9Qo6mFu45PwumsiTddEIcgGbtUm tqlblRZlG4j09MOrigD6iaUnCiKhYhnU0= X-Google-Smtp-Source: AGHT+IFFWL85T9HhS0difffMIWTRJBYYSad8o6w2jHo37Nk+CHybhhZtaAfAEVq3uYrHD502FqqzfA== X-Received: by 2002:a17:902:ea0d:b0:295:7bbd:52fa with SMTP id d9443c01a7336-2957bbd5b0dmr6783025ad.56.1762011856866; Sat, 01 Nov 2025 08:44:16 -0700 (PDT) Received: from localhost.localdomain ([122.171.20.36]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-295269bd2fesm59990105ad.105.2025.11.01.08.44.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 01 Nov 2025 08:44:16 -0700 (PDT) From: Anup Patel To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Greg KH , Alexander Shishkin , Ian Rogers Subject: [PATCH v2 09/12] rvtrace: Add perf driver for tracing using perf tool Date: Sat, 1 Nov 2025 21:12:42 +0530 Message-ID: <20251101154245.162492-10-apatel@ventanamicro.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251101154245.162492-1-apatel@ventanamicro.com> References: <20251101154245.162492-1-apatel@ventanamicro.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20251101_084418_164771_42A0E1FA X-CRM114-Status: GOOD ( 32.04 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Alexandre Ghiti , Atish Patra , Peter Zijlstra , Anup Patel , Adrian Hunter , linux-kernel@vger.kernel.org, Mayuresh Chitale , Ingo Molnar , Jiri Olsa , Anup Patel , Mayuresh Chitale , Namhyung Kim , linux-riscv@lists.infradead.org, Andrew Jones , Liang Kan Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org From: Mayuresh Chitale Add perf driver for RISC-V tracing similar to ARM Coresight and Hisilicon PTT drivers. The driver adds 'rvtrace' event descriptor which can be used by the perf tool to record the RISC-V trace data. Co-developed-by: Anup Patel Signed-off-by: Anup Patel Signed-off-by: Mayuresh Chitale --- drivers/hwtracing/rvtrace/Kconfig | 1 + drivers/hwtracing/rvtrace/Makefile | 2 +- drivers/hwtracing/rvtrace/rvtrace-core.c | 8 + drivers/hwtracing/rvtrace/rvtrace-perf.c | 343 +++++++++++++++++++++++ include/linux/rvtrace.h | 3 + 5 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/rvtrace/rvtrace-perf.c diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/Kconfig index 0577f9acb858..ba11acf1117d 100644 --- a/drivers/hwtracing/rvtrace/Kconfig +++ b/drivers/hwtracing/rvtrace/Kconfig @@ -4,6 +4,7 @@ menuconfig RVTRACE tristate "RISC-V Trace Support" depends on RISCV depends on OF + select PERF_EVENTS default RISCV help This framework provides a kernel interface for the RISC-V trace diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace/Makefile index 122e575da9fb..07403f4d94e3 100644 --- a/drivers/hwtracing/rvtrace/Makefile +++ b/drivers/hwtracing/rvtrace/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RVTRACE) += rvtrace.o -rvtrace-y := rvtrace-core.o rvtrace-platform.o +rvtrace-y := rvtrace-core.o rvtrace-platform.o rvtrace-perf.o obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o diff --git a/drivers/hwtracing/rvtrace/rvtrace-core.c b/drivers/hwtracing/rvtrace/rvtrace-core.c index b955e5f3b048..bbe39aaf930d 100644 --- a/drivers/hwtracing/rvtrace/rvtrace-core.c +++ b/drivers/hwtracing/rvtrace/rvtrace-core.c @@ -777,11 +777,19 @@ static int __init rvtrace_init(void) return ret; } + ret = rvtrace_perf_init(); + if (ret) { + platform_driver_unregister(&rvtrace_platform_driver); + bus_unregister(&rvtrace_bustype); + return ret; + } + return 0; } static void __exit rvtrace_exit(void) { + rvtrace_perf_exit(); platform_driver_unregister(&rvtrace_platform_driver); bus_unregister(&rvtrace_bustype); } diff --git a/drivers/hwtracing/rvtrace/rvtrace-perf.c b/drivers/hwtracing/rvtrace/rvtrace-perf.c new file mode 100644 index 000000000000..2d3039f8b681 --- /dev/null +++ b/drivers/hwtracing/rvtrace/rvtrace-perf.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2025 Ventanamicro Limited. All rights reserved. + * Author: Mayuresh Chitale + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RVTRACE_PMU_NAME "rvtrace" +#define RVTRACE_BUF_LEN (4 * 1024 * 1024) + +static struct pmu rvtrace_pmu; +static DEFINE_SPINLOCK(perf_buf_lock); + +/** + * struct rvtrace_event_data - RISC-V trace specific perf event data + * @work: Handle to free allocated memory outside IRQ context. + * @mask: Hold the CPU(s) this event was set for. + * @aux_hwid_done: Whether a CPU has emitted the TraceID packet or not. + * @path: An array of path, each slot for one CPU. + * @buf: Aux buffer / pages allocated by perf framework. + */ +struct rvtrace_event_data { + struct work_struct work; + cpumask_t mask; + cpumask_t aux_hwid_done; + struct rvtrace_path * __percpu *path; + struct rvtrace_perf_auxbuf buf; +}; + +struct rvtrace_ctxt { + struct perf_output_handle handle; + struct rvtrace_event_data *event_data; +}; + +static DEFINE_PER_CPU(struct rvtrace_ctxt, rvtrace_ctxt); + +static void *alloc_event_data(int cpu) +{ + struct rvtrace_event_data *event_data; + cpumask_t *mask; + + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); + if (!event_data) + return NULL; + + /* Update mask as per selected CPUs */ + mask = &event_data->mask; + if (cpu != -1) + cpumask_set_cpu(cpu, mask); + else + cpumask_copy(mask, cpu_present_mask); + + event_data->path = alloc_percpu(struct rvtrace_path *); + return event_data; +} + +static void rvtrace_free_aux(void *data) +{ + struct rvtrace_event_data *event_data = data; + + schedule_work(&event_data->work); +} + +static struct rvtrace_path **rvtrace_event_cpu_path_ptr(struct rvtrace_event_data *data, + int cpu) +{ + return per_cpu_ptr(data->path, cpu); +} + +static void free_event_data(struct work_struct *work) +{ + struct rvtrace_event_data *event_data; + struct rvtrace_path *path; + cpumask_t *mask; + int cpu; + + event_data = container_of(work, struct rvtrace_event_data, work); + mask = &event_data->mask; + for_each_cpu(cpu, mask) { + path = *rvtrace_event_cpu_path_ptr(event_data, cpu); + rvtrace_destroy_path(path); + } + free_percpu(event_data->path); + kfree(event_data); +} + +static void *rvtrace_setup_aux(struct perf_event *event, void **pages, + int nr_pages, bool overwrite) +{ + struct rvtrace_event_data *event_data = NULL; + struct page **pagelist; + int cpu = event->cpu, i; + cpumask_t *mask; + + event_data = alloc_event_data(cpu); + if (!event_data) + return NULL; + + INIT_WORK(&event_data->work, free_event_data); + mask = &event_data->mask; + /* + * Create the path for each CPU in the mask. In case of any failure skip the CPU + */ + for_each_cpu(cpu, mask) { + struct rvtrace_component *src; + struct rvtrace_path *path; + + src = rvtrace_cpu_source(cpu); + if (!src) + continue; + + path = rvtrace_create_path(src, NULL, RVTRACE_COMPONENT_MODE_PERF); + if (!path) + continue; + + *rvtrace_event_cpu_path_ptr(event_data, cpu) = path; + } + + /* If we don't have any CPUs ready for tracing, abort */ + cpu = cpumask_first(&event_data->mask); + if (cpu >= nr_cpu_ids) + goto err; + + pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL); + if (!pagelist) + goto err; + + for (i = 0; i < nr_pages; i++) + pagelist[i] = virt_to_page(pages[i]); + + event_data->buf.base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL); + if (!event_data->buf.base) { + kfree(pagelist); + goto err; + } + + event_data->buf.nr_pages = nr_pages; + event_data->buf.length = nr_pages * PAGE_SIZE; + event_data->buf.pos = 0; + return event_data; +err: + rvtrace_free_aux(event_data); + return NULL; +} + +static void rvtrace_event_read(struct perf_event *event) +{ +} + +static void rvtrace_event_destroy(struct perf_event *event) +{ +} + +static int rvtrace_event_init(struct perf_event *event) +{ + if (event->attr.type != rvtrace_pmu.type) + return -EINVAL; + + event->destroy = rvtrace_event_destroy; + return 0; +} + +static void rvtrace_event_start(struct perf_event *event, int flags) +{ + struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt); + struct perf_output_handle *handle = &ctxt->handle; + struct rvtrace_event_data *event_data; + int cpu = smp_processor_id(); + struct rvtrace_path *path; + + if (WARN_ON(ctxt->event_data)) + goto fail; + + /* + * Deal with the ring buffer API and get a handle on the + * session's information. + */ + event_data = perf_aux_output_begin(handle, event); + if (!event_data) + goto fail; + + if (!cpumask_test_cpu(cpu, &event_data->mask)) + goto out; + + event_data->buf.pos = handle->head % event_data->buf.length; + path = *rvtrace_event_cpu_path_ptr(event_data, cpu); + if (!path) { + pr_err("Error. Path not found\n"); + return; + } + + if (rvtrace_path_start(path)) { + pr_err("Error. Tracing not started\n"); + return; + } + + /* + * output cpu / trace ID in perf record, once for the lifetime + * of the event. + */ + if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) { + cpumask_set_cpu(cpu, &event_data->aux_hwid_done); + perf_report_aux_output_id(event, cpu); + } + +out: + /* Tell the perf core the event is alive */ + event->hw.state = 0; + ctxt->event_data = event_data; + return; +fail: + event->hw.state = PERF_HES_STOPPED; +} + +static void rvtrace_event_stop(struct perf_event *event, int mode) +{ + struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt); + struct perf_output_handle *handle = &ctxt->handle; + struct rvtrace_event_data *event_data; + int ret, cpu = smp_processor_id(); + struct rvtrace_path *path; + size_t size; + + if (event->hw.state == PERF_HES_STOPPED) + return; + + if (handle->event && + WARN_ON(perf_get_aux(handle) != ctxt->event_data)) + return; + + event_data = ctxt->event_data; + ctxt->event_data = NULL; + + if (WARN_ON(!event_data)) + return; + + if (handle->event && (mode & PERF_EF_UPDATE) && !cpumask_test_cpu(cpu, &event_data->mask)) { + event->hw.state = PERF_HES_STOPPED; + perf_aux_output_end(handle, 0); + return; + } + + /* stop tracing */ + path = *rvtrace_event_cpu_path_ptr(event_data, cpu); + if (!path) { + pr_err("Error. Path not found\n"); + return; + } + + if (rvtrace_path_stop(path)) { + pr_err("Error. Tracing not stopped\n"); + return; + } + + event->hw.state = PERF_HES_STOPPED; + if (handle->event && (mode & PERF_EF_UPDATE)) { + if (WARN_ON_ONCE(handle->event != event)) + return; + spin_lock(&perf_buf_lock); + ret = rvtrace_path_copyto_auxbuf(path, &event_data->buf, &size); + spin_unlock(&perf_buf_lock); + WARN_ON_ONCE(ret); + if (READ_ONCE(handle->event)) + perf_aux_output_end(handle, size); + else + WARN_ON(size); + } +} + +static int rvtrace_event_add(struct perf_event *event, int mode) +{ + struct hw_perf_event *hwc = &event->hw; + int ret = 0; + + if (mode & PERF_EF_START) { + rvtrace_event_start(event, 0); + if (hwc->state & PERF_HES_STOPPED) + ret = -EINVAL; + } else { + hwc->state = PERF_HES_STOPPED; + } + + return ret; +} + +static void rvtrace_event_del(struct perf_event *event, int mode) +{ + rvtrace_event_stop(event, PERF_EF_UPDATE); +} + +PMU_FORMAT_ATTR(event, "config:0-0"); + +static struct attribute *rvtrace_pmu_formats_attr[] = { + &format_attr_event.attr, + NULL, +}; + +static struct attribute_group rvtrace_pmu_format_group = { + .name = "format", + .attrs = rvtrace_pmu_formats_attr, +}; + +static const struct attribute_group *rvtrace_pmu_attr_groups[] = { + &rvtrace_pmu_format_group, + NULL, +}; + +int __init rvtrace_perf_init(void) +{ + rvtrace_pmu.capabilities = (PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE); + rvtrace_pmu.attr_groups = rvtrace_pmu_attr_groups; + rvtrace_pmu.task_ctx_nr = perf_sw_context; + rvtrace_pmu.read = rvtrace_event_read; + rvtrace_pmu.event_init = rvtrace_event_init; + rvtrace_pmu.setup_aux = rvtrace_setup_aux; + rvtrace_pmu.free_aux = rvtrace_free_aux; + rvtrace_pmu.start = rvtrace_event_start; + rvtrace_pmu.stop = rvtrace_event_stop; + rvtrace_pmu.add = rvtrace_event_add; + rvtrace_pmu.del = rvtrace_event_del; + rvtrace_pmu.module = THIS_MODULE; + + return perf_pmu_register(&rvtrace_pmu, RVTRACE_PMU_NAME, -1); +} + +void __exit rvtrace_perf_exit(void) +{ + perf_pmu_unregister(&rvtrace_pmu); +} diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h index cecf6c153ca6..d5f782ac132f 100644 --- a/include/linux/rvtrace.h +++ b/include/linux/rvtrace.h @@ -343,4 +343,7 @@ static inline int rvtrace_comp_is_empty(struct rvtrace_component *comp) comp->pdata->control_poll_timeout_usecs); } +int rvtrace_perf_init(void); +void rvtrace_perf_exit(void); + #endif -- 2.43.0 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv