platform: dce: Add Debugfs support for Perf

Add debugfs nodes
 - start/stop perf stats.
 - read perf stat stats
 - set the format type as csv or xml

Add debugfs to capture perf events
 - Can enable specific perf events
through debugfs node.

Signed-off-by: Mahesh Kumar <mahkumar@nvidia.com>
Change-Id: I0a7833d7a8f04296ba3806f4f2a218175080d2e2
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2765513
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
Reviewed-by: Arun Swain <arswain@nvidia.com>
Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com>
Tested-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Mahesh Kumar
2022-07-11 15:06:14 +05:30
committed by Laxman Dewangan
parent b25786578c
commit 873e421d95
10 changed files with 1015 additions and 23 deletions

View File

@@ -5,6 +5,7 @@
#
GCOV_PROFILE := y
ccflags-y += -I$(srctree.nvidia)/drivers/platform/tegra/dce/include
ccflags-y += -Wno-multichar
ccflags-y += -Werror
ccflags-y += -Wno-error=cpp
@@ -39,7 +40,7 @@ tegra-dce-$(CONFIG_TEGRA_DCE) += \
ifeq ($(CONFIG_DEBUG_FS),y)
tegra-dce-$(CONFIG_TEGRA_DCE) += \
dce-debug.o
dce-debug.o \
dce-admin-debug.o \
dce-debug-perf.o
endif
ccflags-y += -I$(srctree.nvidia-t23x)/drivers/platform/tegra/dce/include

View File

@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <dce.h>
#include <dce-mailbox.h>
#include <dce-util-common.h>
#include <dce-client-ipc-internal.h>
#include <interface/dce-core-interface-errors.h>
#include <interface/dce-interface.h>
#include <interface/dce-admin-cmds.h>
/**
* dce_admin_send_cmd_set_perf_stat - Start/stop DCE perf data collection.
*
* @d - Pointer to tegra_dce struct.
* @msg - Pointer to dce_ipc_msg struct.
* @start_perf - Bool to indicate start/stop of perf data collection
*
* Return - 0 if successful
*/
int dce_admin_send_cmd_set_perf_stat(struct tegra_dce *d,
struct dce_ipc_message *msg,
bool start_perf)
{
int ret = -1;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
if (!msg || !msg->tx.data || !msg->rx.data)
goto out;
/* return if dce bootstrap not completed */
if (!dce_is_bootstrap_done(d)) {
dce_err(d, "Admin Bootstrap not yet done");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
if (start_perf == true)
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_START;
else
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_STOP;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending set perf msg : [%d]", ret);
goto out;
}
out:
return ret;
}
/**
* dce_admin_send_cmd_get_perf_stat - Get DCE perf data.
*
* @d - Pointer to tegra_dce struct.
* @msg - Pointer to dce_ipc_msg struct.
*
* Return - 0 if successful
*/
int dce_admin_send_cmd_get_perf_stat(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = -1;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
if (!msg || !msg->tx.data || !msg->rx.data)
goto out;
/* return if dce bootstrap not completed */
if (!dce_is_bootstrap_done(d)) {
dce_err(d, "Admin Bootstrap not yet done");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_RESULTS;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending get perf msg : [%d]", ret);
goto out;
}
out:
return ret;
}
int dce_admin_send_cmd_get_perf_events(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = -1;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
if (!msg || !msg->tx.data || !msg->rx.data)
goto out;
/* return if dce bootstrap not completed */
if (!dce_is_bootstrap_done(d)) {
dce_err(d, "Admin Bootstrap not yet done");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_GET_EVENTS;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending get perf events msg : [%d]", ret);
goto out;
}
out:
return ret;
}
int dce_admin_send_cmd_clear_perf_events(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = -1;
struct dce_admin_ipc_cmd *req_msg;
if (!msg || !msg->tx.data || !msg->rx.data)
goto out;
/* return if dce bootstrap not completed */
if (!dce_is_bootstrap_done(d)) {
dce_err(d, "Admin Bootstrap not yet done");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_CLEAR_EVENTS;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending clear perf events msg : [%d]", ret);
goto out;
}
out:
return ret;
}

View File

@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,

View File

@@ -0,0 +1,549 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/errno.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <dce.h>
#include <dce-log.h>
#include <dce-util-common.h>
#include <interface/dce-interface.h>
#define DCE_PERF_OUTPUT_FORMAT_CSV ((uint32_t)(0U))
#define DCE_PERF_OUTPUT_FORMAT_XML ((uint32_t)(1U))
static uint32_t perf_output_format = DCE_PERF_OUTPUT_FORMAT_CSV;
ssize_t dbg_dce_perf_stats_stats_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int ret = 0;
bool start_perf;
struct dce_ipc_message *msg = NULL;
struct dce_admin_ipc_cmd *req_msg;
struct tegra_dce *d = ((struct seq_file *)file->private_data)->private;
ret = kstrtobool_from_user(user_buf, 3ULL, &start_perf);
if (ret) {
dce_err(d, "Unable to parse start/stop for dce perf stats");
goto out;
}
msg = dce_admin_allocate_message(d);
if (!msg) {
dce_err(d, "IPC msg allocation failed");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
/*
* Collect All Perfs
* Clear previous data and start collecting new perf data
*
* TODO: Add support to collect specific perf data
*/
req_msg->args.perf.perf_cmd.enable = DCE_ADMIN_PERF_STATS_ALL;
req_msg->args.perf.perf_cmd.clear = DCE_ADMIN_PERF_CLEAR_CLEAR;
/*
* echo "1/y" (kstrtobool true) to start perf data collection
* echo "0/n" (kstrtobool false) to stop perf data collection
*/
ret = dce_admin_send_cmd_set_perf_stat(d, msg, start_perf);
if (ret) {
dce_err(d, "Failed to Set perf stat\n");
goto out;
}
dce_debug(d, "DCE perf stats collection %s", start_perf ? "started" : "stopped");
out:
dce_admin_free_message(d, msg);
return count;
}
static void dbg_dce_perf_stats_sched_task_xml(struct seq_file *s, struct tegra_dce *d,
const struct dce_admin_task_stats *task_stat)
{
/* Don't print task stats if task name is not set */
if (task_stat->name == NULL)
return;
seq_printf(s, "<task><name>%s</name>", task_stat->name);
seq_printf(s, "<init>%llu</init><ready>%llu</ready>",
task_stat->init, task_stat->ready);
seq_printf(s, "<ready_time>%llu</ready_time>",
task_stat->ready_time);
seq_printf(s, "<dcache_misses>%llu</dcache_misses><instr_exec_count>%llu</instr_exec_count>",
task_stat->dcache_misses, task_stat->inst_exec);
seq_printf(s, "<mmio_req_count>%llu</mmio_req_count>",
task_stat->mmio_req);
seq_printf(s, "<stat><name>sleep</name><accumulate>%llu</accumulate>",
task_stat->sleep.accumulate);
seq_printf(s, "<iterations>%llu</iterations><min>%llu</min><max>%llu</max></stat>",
task_stat->sleep.iterations,
task_stat->sleep.min,
task_stat->sleep.max);
seq_printf(s, "<stat><name>run</name><accumulate>%llu</accumulate>",
task_stat->run.accumulate);
seq_printf(s, "<iterations>%llu</iterations><min>%llu</min><max>%llu</max></stat>",
task_stat->run.iterations,
task_stat->run.min,
task_stat->run.max);
seq_printf(s, "<stat><name>run-context</name><accumulate>%llu</accumulate>",
task_stat->run_context.accumulate);
seq_printf(s, "<iterations>%llu</iterations><min>%llu</min><max>%llu</max></stat>",
task_stat->run_context.iterations,
task_stat->run_context.min,
task_stat->run_context.max);
seq_puts(s, "</task>\n");
}
static void dbg_dce_perf_stats_pm_event_xml(struct seq_file *s,
const struct dce_admin_pm_event_stats *pm_evt)
{
seq_printf(s, "<start>%llu</start><end>%llu</end>\n", pm_evt->start, pm_evt->end);
seq_printf(s, "<count>%llu</count>\n", pm_evt->count);
}
static void dbg_dce_perf_stats_show_xml(struct seq_file *s, struct tegra_dce *d,
struct dce_admin_perf_info *perf)
{
const struct dce_admin_pm_event_stats *pm_evt;
const struct dce_admin_sched_stats *sched = &perf->sched;
uint32_t i;
seq_puts(s, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
seq_puts(s, "<PerfData>");
seq_printf(s, "<tcm_ready>%llu</tcm_ready>\n", perf->tcm_ready);
seq_printf(s, "<sched_start>%llu</sched_start>\n", perf->sched_start);
seq_printf(s, "<signaled_ready>%llu</signaled_ready>\n", perf->ready_time);
/*
* dCache-misses Perf Data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_DCACHE_MISSES];
seq_puts(s, "<dcache-misses>\n");
dbg_dce_perf_stats_pm_event_xml(s, pm_evt);
seq_puts(s, "</dcache-misses>\n");
/*
* instruction execution Perf Data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_INSTR_EXEC];
seq_puts(s, "<instr-execution>\n");
dbg_dce_perf_stats_pm_event_xml(s, pm_evt);
seq_puts(s, "</instr-execution>\n");
/*
* instruction execution Perf Data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_MEM_REQ];
seq_puts(s, "<mmio-request>\n");
dbg_dce_perf_stats_pm_event_xml(s, pm_evt);
seq_puts(s, "</mmio-request>\n");
/*
* Print Scheduler Perf data and Per task data
*/
seq_puts(s, "<sched>\n");
seq_printf(s, "<start>%llu</start><end>%llu</end>", sched->start, sched->end);
seq_printf(s, "<context_switches>%llu</context_switches>\n", sched->context_switches);
for (i = 0U; i < DCE_ADMIN_PERF_ACTIVE_TASKS_NUM; i += 1U) {
dbg_dce_perf_stats_sched_task_xml(s, d,
&sched->tasks[i]);
}
seq_puts(s, "</sched>"); //sched perf data done
seq_puts(s, "</PerfData>\n");
}
static void dbg_dce_perf_stats_sched_task_csv(struct seq_file *s, struct tegra_dce *d,
const struct dce_admin_task_stats *task_stat)
{
/* Don't print if task_name is not set */
if (task_stat->name == NULL)
return;
seq_printf(s, "\"%s\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"sleep\",\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n",
task_stat->name, task_stat->init, task_stat->ready,
task_stat->ready_time, task_stat->dcache_misses,
task_stat->inst_exec, task_stat->mmio_req,
task_stat->sleep.accumulate,
task_stat->sleep.iterations, task_stat->sleep.min,
task_stat->sleep.max);
seq_puts(s, "\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"run\",");
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n",
task_stat->run.accumulate, task_stat->run.iterations,
task_stat->run.min, task_stat->run.max);
seq_puts(s, "\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"run-context\",");
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n",
task_stat->run_context.accumulate,
task_stat->run_context.iterations,
task_stat->run_context.min,
task_stat->run_context.max);
}
static void dbg_dce_perf_stats_show_csv(struct seq_file *s, struct tegra_dce *d,
struct dce_admin_perf_info *perf)
{
const struct dce_admin_pm_event_stats *pm_evt;
const struct dce_admin_sched_stats *sched = &perf->sched;
uint32_t i;
seq_puts(s, "\"tcm-ready\",\"sched-start\",\"signaled-ready\",");
seq_puts(s, "\"dcache:start-time\",\"dcache:end-time\",\"dcache:miss_cnt\",");
seq_puts(s, "\"inst_exec:start-time\",\"inst_exec:end-time\",\"inst_exec:count\",");
seq_puts(s, "\"mmio_req:start-time\",\"mmio_req:end-time\",\"mmio_req:count\",");
seq_puts(s, "\"sched:start-time\",\"sched:end-time\",\"sched:context_switches\"\n");
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",", perf->tcm_ready,
perf->sched_start, perf->ready_time);
/*
* Print Cache Perf data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_DCACHE_MISSES];
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",",
pm_evt->start, pm_evt->end, pm_evt->count);
/*
* Print Instruction exec Perf data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_INSTR_EXEC];
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",",
pm_evt->start, pm_evt->end, pm_evt->count);
/*
* Print Instruction exec Perf data
*/
pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_MEM_REQ];
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",",
pm_evt->start, pm_evt->end, pm_evt->count);
/*
* Print Scheduler Perf data and Per task data
*/
seq_printf(s, "\"%llu\",\"%llu\",\"%llu\"\n",
sched->start, sched->end,
sched->context_switches);
seq_puts(s, "\"sched:task_name\",\"sched:task-init\",\"sched:task-ready\",");
seq_puts(s, "\"sched:task-ready-time\",\"sched:task-stat:dcache-misses\",");
seq_puts(s, "\"sched:task-stat:instr_exec\",\"sched:task-stat:mem_req\",");
seq_puts(s, "\"sched:task-stat:name\",");
seq_puts(s, "\"sched:task-stat:accumulate\",\"sched:task-stat:iterations\",");
seq_puts(s, "\"sched:task-stat:min\",\"sched:task-stat:max\"\n");
for (i = 0U; i < DCE_ADMIN_PERF_ACTIVE_TASKS_NUM; i += 1U) {
dbg_dce_perf_stats_sched_task_csv(s, d,
&sched->tasks[i]);
}
}
static int dbg_dce_perf_stats_stats_fops_show(struct seq_file *s, void *data)
{
int ret = 0;
struct dce_ipc_message *msg = NULL;
struct dce_admin_ipc_resp *resp_msg;
struct dce_admin_perf_info *perf;
struct tegra_dce *d = (struct tegra_dce *)s->private;
msg = dce_admin_allocate_message(d);
if (!msg) {
dce_err(d, "IPC msg allocation failed");
goto out;
}
ret = dce_admin_send_cmd_get_perf_stat(d, msg);
if (ret) {
dce_err(d, "Failed to Get perf stat\n");
goto out;
}
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
perf = &resp_msg->args.perf.info.sched_stats;
if (perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV)
dbg_dce_perf_stats_show_csv(s, d, perf);
else
dbg_dce_perf_stats_show_xml(s, d, perf);
out:
dce_admin_free_message(d, msg);
return 0;
}
int dbg_dce_perf_stats_stats_fops_open(struct inode *inode,
struct file *file)
{
return single_open(file, dbg_dce_perf_stats_stats_fops_show,
inode->i_private);
}
/*
* Debugfs nodes for displaying a help message about perf stats capture
*/
static int dbg_dce_perf_stats_help_fops_show(struct seq_file *s, void *data)
{
/**
* Writing
* '0' to /sys/kernel/debug/tegra_dce/perf/stats/stats
* - Clear the Perf stats data
* '1' to /sys/kernel/debug/tegra_dce/perf/stats/stats
* - Start the Perf stats data
* To capture fresh Perf stats, first clear and Start.
*
* The result can be printed in csv or xml format
*
* '0' to /sys/kernel/debug/tegra_dce/perf/format
* - Data is collected in CSV format
* '1' to /sys/kernel/debug/tegra_dce/perf/format
* - Data is collected in XML format
* Default is set for CSV
*
* cat /sys/kernel/debug/tegra_dce/perf/stats/stats
* - get the perf data in selected format
*
*/
seq_printf(s, "DCE Perf Capture\n"
"----------------------\n"
" echo '0' > perf/stats/stats : Clear perf data\n"
" cat perf/stats/stats : get perf data in selected format\n"
" echo <0/1> > perf/format : get data in CSV/XML format\n");
return 0;
}
int dbg_dce_perf_stats_help_fops_open(struct inode *inode,
struct file *file)
{
return single_open(file, dbg_dce_perf_stats_help_fops_show,
inode->i_private);
}
ssize_t dbg_dce_perf_format_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int ret = 0;
u32 format;
struct tegra_dce *d = ((struct seq_file *)file->private_data)->private;
ret = kstrtou32_from_user(user_buf, count, 10, &format);
if (ret) {
dce_err(d, "Invalid format!");
goto done;
}
if (format == DCE_PERF_OUTPUT_FORMAT_CSV)
perf_output_format = DCE_PERF_OUTPUT_FORMAT_CSV;
else if (format == DCE_PERF_OUTPUT_FORMAT_XML)
perf_output_format = DCE_PERF_OUTPUT_FORMAT_XML;
else
dce_err(d, "Invalid format [%u]!", format);
done:
return count;
}
static int dbg_dce_perf_format_fops_show(struct seq_file *s, void *data)
{
seq_printf(s, "Supported Formats\n"
"----------------------\n"
"CSV:0 XML:1\n"
"Current format:%s\n",
(perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV) ? "CSV" : "XML");
return 0;
}
int dbg_dce_perf_format_fops_open(struct inode *inode, struct file *file)
{
return single_open(file, dbg_dce_perf_format_fops_show,
inode->i_private);
}
/*
* Debugfs nodes for displaying a help message about perf stats capture
*/
static int dbg_dce_perf_events_help_fops_show(struct seq_file *s, void *data)
{
/**
* Writing
* '0' to /sys/kernel/debug/tegra_dce/perf/events/events
* - Clear the Perf Events data
* 'Bit masked value' to /kernel/debug/tegra_dce/perf/events/events
* - Enable an event for the Perf Events data capture.
* Clear bit mask if the event perf is not needed to be captured.
*
* The result can be printed in csv or xml format
*
* '0' to /sys/kernel/debug/tegra_dce/perf/format
* - Data is collected in CSV format
* '1' to /sys/kernel/debug/tegra_dce/perf/format
* - Data is collected in XML format
* Default is set for CSV
*
* cat /sys/kernel/debug/tegra_dce/perf/events/events
* - get the perf event data in selected format
* - shows event name/ accumulated time/ iterations happened
* - min and max time taken to run an event iteration
*/
seq_printf(s, "DCE Perf Events Capture\n"
"----------------------\n"
" echo '0' > perf/events/events: Clear perf events data\n"
" echo 'bit masked value' > perf/events/events: Enable perf event capture\n"
" cat perf/events/events: get perf events data in selected format\n"
" echo <0/1> > perf/format: get data in CSV/XML format\n");
return 0;
}
int dbg_dce_perf_events_help_fops_open(struct inode *inode,
struct file *file)
{
return single_open(file, dbg_dce_perf_events_help_fops_show,
inode->i_private);
}
ssize_t dbg_dce_perf_events_events_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int ret = 0;
uint32_t events;
struct dce_ipc_message *msg = NULL;
struct dce_admin_ipc_cmd *req_msg;
struct tegra_dce *d = ((struct seq_file *)file->private_data)->private;
/*
* Writing "0/n" will clear the events
* Write Bit Masked value to enable events
*/
ret = kstrtou32_from_user(user_buf, count, 0, &events);
if (ret) {
dce_err(d, "Unable to parse for event perf stats");
goto out;
}
msg = dce_admin_allocate_message(d);
if (!msg) {
dce_err(d, "IPC msg allocation failed");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
if (events == 0x0U) {
// Clear event
req_msg->args.perf.event_cmd.clear = DCE_ADMIN_EVENT_CLEAR_CLEAR;
} else {
// Disable all Events if enable : 0x80000000
// Elase enable only Bit masked events
req_msg->args.perf.event_cmd.clear = DCE_ADMIN_EVENT_CLEAR_RETAIN;
req_msg->args.perf.event_cmd.enable = events;
}
ret = dce_admin_send_cmd_clear_perf_events(d, msg);
if (ret) {
dce_err(d, "Failed to Clear perf events\n");
goto out;
}
out:
dce_admin_free_message(d, msg);
return count;
}
static void dbg_dce_perf_events_show_csv(struct seq_file *s, struct tegra_dce *d,
const struct dce_admin_event_info *events)
{
int i = 0;
seq_puts(s, "\"Evt Name\",\"accumulate\",\"iterations\",\"min\",\"max\"\n");
for (i = 0; i < DCE_ADMIN_NUM_EVENTS; i++) {
if (events->events[i].name == NULL)
continue;
seq_printf(s, "\"%s\",\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n",
events->events[i].name, events->events[i].event.accumulate,
events->events[i].event.iterations, events->events[i].event.min,
events->events[i].event.max);
}
}
static void dbg_dce_perf_events_show_xml(struct seq_file *s, struct tegra_dce *d,
const struct dce_admin_event_info *events)
{
int i = 0;
seq_puts(s, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
seq_puts(s, "<EventsPerfData>");
for (i = 0; i < DCE_ADMIN_NUM_EVENTS; i++) {
if (events->events[i].name == NULL)
continue;
seq_printf(s, "<name>%s</name>\n", events->events[i].name);
seq_printf(s, "<accumulate>%llu</accumulate>\n",
events->events[i].event.accumulate);
seq_printf(s, "<iterations>%llu</iterations>\n",
events->events[i].event.iterations);
seq_printf(s, "<min>%llu</min>\n", events->events[i].event.min);
seq_printf(s, "<max>%llu</max>\n", events->events[i].event.max);
}
seq_puts(s, "</EventsPerfData>\n");
}
static int dbg_dce_perf_events_events_fops_show(struct seq_file *s, void *data)
{
int ret = 0;
struct dce_ipc_message *msg = NULL;
struct dce_admin_ipc_resp *resp_msg;
struct dce_admin_event_info *events;
struct tegra_dce *d = (struct tegra_dce *)s->private;
msg = dce_admin_allocate_message(d);
if (!msg) {
dce_err(d, "IPC msg allocation failed");
goto out;
}
ret = dce_admin_send_cmd_get_perf_events(d, msg);
if (ret) {
dce_err(d, "Failed to Get perf stat\n");
goto out;
}
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
events = &resp_msg->args.perf.info.events_stats;
if (perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV)
dbg_dce_perf_events_show_csv(s, d, events);
else
dbg_dce_perf_events_show_xml(s, d, events);
out:
dce_admin_free_message(d, msg);
return 0;
}
int dbg_dce_perf_events_events_fops_open(struct inode *inode,
struct file *file)
{
return single_open(file, dbg_dce_perf_events_events_fops_show,
inode->i_private);
}

View File

@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -17,6 +18,7 @@
#include <dce.h>
#include <dce-log.h>
#include <dce-util-common.h>
#include <dce-debug-perf.h>
#include <interface/dce-interface.h>
#include <interface/dce-core-interface-errors.h>
@@ -612,6 +614,44 @@ static const struct file_operations boot_status_fops = {
.read = dbg_dce_boot_status_fops_read,
};
static const struct file_operations perf_format_fops = {
.open = dbg_dce_perf_format_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = dbg_dce_perf_format_fops_write,
};
static const struct file_operations perf_stats_stats_fops = {
.open = dbg_dce_perf_stats_stats_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = dbg_dce_perf_stats_stats_fops_write,
};
static const struct file_operations perf_stats_help_fops = {
.open = dbg_dce_perf_stats_help_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations perf_events_events_fops = {
.open = dbg_dce_perf_events_events_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = dbg_dce_perf_events_events_fops_write,
};
static const struct file_operations perf_events_help_fops = {
.open = dbg_dce_perf_events_help_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void dce_remove_debug(struct tegra_dce *d)
{
struct dce_device *d_dev = dce_device_from_dce(d);
@@ -699,6 +739,7 @@ void dce_init_debug(struct tegra_dce *d)
struct device *dev = dev_from_dce(d);
struct dce_device *d_dev = dce_device_from_dce(d);
struct dentry *debugfs_dir = NULL;
struct dentry *perf_debugfs_dir = NULL;
d_dev->debugfs = debugfs_create_dir("tegra_dce", NULL);
if (!d_dev->debugfs)
@@ -734,6 +775,43 @@ void dce_init_debug(struct tegra_dce *d)
if (!retval)
goto err_handle;
perf_debugfs_dir = debugfs_create_dir("perf", d_dev->debugfs);
if (!perf_debugfs_dir)
goto err_handle;
retval = debugfs_create_file("format", 0644,
perf_debugfs_dir, d, &perf_format_fops);
if (!retval)
goto err_handle;
debugfs_dir = debugfs_create_dir("stats", perf_debugfs_dir);
if (!debugfs_dir)
goto err_handle;
retval = debugfs_create_file("stats", 0644,
debugfs_dir, d, &perf_stats_stats_fops);
if (!retval)
goto err_handle;
retval = debugfs_create_file("help", 0444,
debugfs_dir, d, &perf_stats_help_fops);
if (!retval)
goto err_handle;
debugfs_dir = debugfs_create_dir("events", perf_debugfs_dir);
if (!debugfs_dir)
goto err_handle;
retval = debugfs_create_file("events", 0644,
debugfs_dir, d, &perf_events_events_fops);
if (!retval)
goto err_handle;
retval = debugfs_create_file("help", 0444,
debugfs_dir, d, &perf_events_help_fops);
if (!retval)
goto err_handle;
retval = debugfs_create_file("dump_hsp_regs", 0444,
d_dev->debugfs, d, &dump_hsp_regs_fops);
if (!retval)

View File

@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef DCE_DEBUG_PERF_H
#define DCE_DEBUG_PERF_H
#include <linux/debugfs.h>
#include <linux/uaccess.h>
int dbg_dce_perf_stats_stats_fops_open(struct inode *inode,
struct file *file);
ssize_t dbg_dce_perf_stats_stats_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos);
int dbg_dce_perf_stats_help_fops_open(struct inode *inode,
struct file *file);
ssize_t dbg_dce_perf_format_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos);
int dbg_dce_perf_format_fops_open(struct inode *inode, struct file *file);
int dbg_dce_perf_events_events_fops_open(struct inode *inode,
struct file *file);
ssize_t dbg_dce_perf_events_events_fops_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos);
int dbg_dce_perf_events_help_fops_open(struct inode *inode,
struct file *file);
#endif

View File

@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -34,7 +35,7 @@
#define DCE_DISPRM_CMD_MAX_FSIZE 4096U
#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES 4U
#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE 4096U
#define DCE_ADMIN_CMD_MAX_FSIZE 1024U
#define DCE_ADMIN_CMD_MAX_FSIZE 2048U
#define DCE_IPC_WAIT_TYPE_INVALID 0U
#define DCE_IPC_WAIT_TYPE_RPC 1U

View File

@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -425,6 +426,17 @@ int dce_admin_send_enter_sc7(struct tegra_dce *d,
struct dce_ipc_message *msg);
int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params);
int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params);
int dce_admin_send_cmd_get_perf_stat(struct tegra_dce *d,
struct dce_ipc_message *msg);
int dce_admin_send_cmd_set_perf_stat(struct tegra_dce *d,
struct dce_ipc_message *msg,
bool start_perf);
int dce_admin_send_cmd_stop_perf_stat(struct tegra_dce *d,
struct dce_ipc_message *msg);
int dce_admin_send_cmd_get_perf_events(struct tegra_dce *d,
struct dce_ipc_message *msg);
int dce_admin_send_cmd_clear_perf_events(struct tegra_dce *d,
struct dce_ipc_message *msg);
int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type);
void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type);

View File

@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2018-2023 NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -24,6 +25,7 @@
#define DCE_ADMIN_CMDS_H
#include <interface/dce-types.h>
#include <interface/dce-admin-perf-stats.h>
/*
* Version of the ADMIN command interface.
@@ -34,7 +36,7 @@
* To keep things simple, this value should be incremented by 1 each
* time changes are made.
*/
#define DCE_ADMIN_VERSION 2
#define DCE_ADMIN_VERSION 3
#define DCE_ADMIN_CMD_SIZE sizeof(struct dce_admin_ipc_cmd)
#define DCE_ADMIN_RESP_SIZE sizeof(struct dce_admin_ipc_resp)
@@ -58,20 +60,22 @@
#define DCE_ADMIN_CMD_GET_LOG_INFO 0x0BU // get current log info
#define DCE_ADMIN_CMD_LOCK_CHANGES 0x0CU // lock creating new channels
// and changing memory areas
#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0DU // start collecting code
// coverage data
#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0EU // stop collecting code
// coverage data
#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0DU // start collecting code
// coverage data
#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0EU // stop collecting code
// coverage data
#define DCE_ADMIN_CMD_PERF_START 0x0FU // start collecting perf data
#define DCE_ADMIN_CMD_PERF_STOP 0x10U // stop collecting perf data
#define DCE_ADMIN_CMD_INT_TEST_START 0x11U // start internal tests
#define DCE_ADMIN_CMD_INT_TEST_STOP 0x12U // stop internal tests and return status
#define DCE_ADMIN_CMD_EXT_TEST 0x13U // run external test (blocking call)
#define DCE_ADMIN_CMD_INT_TEST_START 0x11U // start internal tests
#define DCE_ADMIN_CMD_INT_TEST_STOP 0x12U // stop internal tests and return status
#define DCE_ADMIN_CMD_EXT_TEST 0x13U // run external test (blocking call)
#define DCE_ADMIN_CMD_DEBUG 0x14U // debug command
#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x15U // tell RM to "bootstrap"
#define DCE_ADMIN_CMD_PERF_RESULTS 0x16U // copy out the perf results
#define DCE_ADMIN_CMD_PERF_GET_EVENTS 0x17U // get perf events
#define DCE_ADMIN_CMD_PERF_CLEAR_EVENTS 0x18U // clear perf events
#define DCE_ADMIN_CMD_NEXT 0x16U // must be last command ID + 1
#define DCE_ADMIN_CMD_NEXT 0x19U // must be last command ID + 1
struct dce_admin_version_info {
uint32_t version;
@@ -159,6 +163,7 @@ struct dce_admin_ipc_cmd {
struct dce_admin_ipc_info_args ipc_info;
struct dce_admin_mem_args mem_map;
struct dce_admin_ipc_create_args ipc_create;
struct dce_admin_perf_args perf;
} args;
};
@@ -166,11 +171,12 @@ struct dce_admin_ipc_resp {
uint32_t error;
union {
struct dce_admin_version_info version;
struct dce_admin_echo echo;
struct dce_admin_log_args log;
struct dce_admin_ipc_info ipc;
struct dce_admin_mem_args mem_info;
struct dce_admin_echo echo;
struct dce_admin_log_args log;
struct dce_admin_ipc_info ipc;
struct dce_admin_mem_args mem_info;
struct dce_admin_fw_version_info fw_version;
struct dce_admin_perf_stats_info perf;
} args;
};

View File

@@ -0,0 +1,138 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (c) 2023 NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef DCE_ADMIN_PERF_STATS_H
#define DCE_ADMIN_PERF_STATS_H
/*
* Values for stats field
*/
#define DCE_ADMIN_PERF_STATS_SCHED 0x0001
#define DCE_ADMIN_PERF_STATS_CACHE 0x0002
#define DCE_ADMIN_PERF_STATS_IEXEC 0x0003
#define DCE_ADMIN_PERF_STATS_MMIO 0x0004
#define DCE_ADMIN_PERF_STATS_ALL 0xFFFF
/*
* Values for clear field
*/
#define DCE_ADMIN_PERF_CLEAR_RETAIN 0U
#define DCE_ADMIN_PERF_CLEAR_CLEAR 1U
#define DCE_ADMIN_EVENT_CLEAR_RETAIN 0U
#define DCE_ADMIN_EVENT_CLEAR_CLEAR 1U
struct dce_admin_perf_cmd_args {
uint32_t enable; // Enable stats capture through Bit-Mask
uint32_t clear; // Clear all event stats
};
struct dce_admin_perf_args {
struct dce_admin_perf_cmd_args perf_cmd;
struct dce_admin_perf_cmd_args event_cmd;
};
/*
* Task related defines for perf stats
*/
#define DCE_ADMIN_PERF_ADMIN_TASK 0U
#define DCE_ADMIN_PERF_RM_TASK 1U
#define DCE_ADMIN_PERF_PRINT_TASK 2U
#define DCE_ADMIN_PERF_TCU_TASK 3U
#define DCE_ADMIN_PERF_IDLE_TASK 4U
// Change the active task number on adding more tasks
#define DCE_ADMIN_PERF_ACTIVE_TASKS_NUM 5U
// Max number of tasks in perf stats capture
#define DCE_ADMIN_PERF_NUM_TASKS 10U
// Task name length
#define DCE_ADMIN_TASK_NAME_LEN 16U
struct dce_admin_stat_avg {
uint64_t accumulate; // accumulated delta times
uint64_t iterations;
uint64_t min;
uint64_t max;
};
struct dce_admin_task_stats {
const char name[DCE_ADMIN_TASK_NAME_LEN]; // task name
uint64_t init; // how long to run init code
uint64_t ready; // how long for task to be ready
uint64_t ready_time; // time when task became ready
uint64_t dcache_misses; // dcaches missed count accumulated within this task
uint64_t inst_exec; // instruction execution accumulated
uint64_t mmio_req; // memory request accumulated
struct dce_admin_stat_avg sleep;
struct dce_admin_stat_avg run;
struct dce_admin_stat_avg run_context; // running context
};
struct dce_admin_sched_stats {
uint64_t start; // time collection started
uint64_t end; // time collection stopped
uint64_t context_switches; // overall context switches
struct dce_admin_task_stats tasks[DCE_ADMIN_PERF_NUM_TASKS];
};
/*
* Performance monitor events and counters
*/
#define DCE_ADMIN_PM_EVT_DCACHE_MISSES 0U
#define DCE_ADMIN_PM_EVT_INSTR_EXEC 1U
#define DCE_ADMIN_PM_EVT_MEM_REQ 2U
#define DCE_ADMIN_PM_EVT_COUNTERS 3U
struct dce_admin_pm_event_stats {
uint64_t start; // time collection started
uint64_t end; // time collection stopped
uint64_t count; // Event counter accumulated
};
struct dce_admin_perf_info {
uint64_t tcm_ready; // tcm is ready/copied
uint64_t sched_start; // scheduler started
uint64_t ready_time; // signaled ready
struct dce_admin_pm_event_stats pm_events[DCE_ADMIN_PM_EVT_COUNTERS];
struct dce_admin_sched_stats sched;
};
#define DCE_ADMIN_EVENT_NAME_LEN 32U // max length of an event name
#define DCE_ADMIN_NUM_EVENTS 20U // s.b. same as DCE_NUM_EVENTS
struct dce_admin_event {
char name[DCE_ADMIN_EVENT_NAME_LEN];
struct dce_admin_stat_avg event;
};
struct dce_admin_event_info {
struct dce_admin_event events[DCE_ADMIN_NUM_EVENTS];
};
struct dce_admin_perf_stats_info {
union {
struct dce_admin_perf_info sched_stats;
struct dce_admin_event_info events_stats;
} info;
};
#endif