Commit c4da5eea authored by Bibo Mao's avatar Bibo Mao Committed by Xianglai Li
Browse files

perf kvm: Add kvm-stat for loongarch64

mainline inclusion
from mainline-v6.11-rc1
commit 492ac37fa38faf520b5beae44c930063265ee183
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IAZJDO


CVE: NA

--------------------------------

Add support for 'perf kvm stat' on loongarch64 platform, now only kvm
exit event is supported.

Here is example output about "perf kvm --host stat report" command

   Event name   Samples   Sample%     Time (ns)   Time%   Mean Time (ns)
    Mem Store     83969    51.00%     625697070   8.00%             7451
     Mem Read     37641    22.00%     112485730   1.00%             2988
    Interrupt     15542     9.00%      20620190   0.00%             1326
        IOCSR     15207     9.00%      94296190   1.00%             6200
    Hypercall      4873     2.00%      12265280   0.00%             2516
         Idle      3713     2.00%    6322055860  87.00%          1702681
          FPU      1819     1.00%       2750300   0.00%             1511
   Inst Fetch       502     0.00%       1341740   0.00%             2672
   Mem Modify       324     0.00%        602240   0.00%             1858
       CPUCFG        55     0.00%         77610   0.00%             1411
          CSR        12     0.00%         19690   0.00%             1640
         LASX         3     0.00%          4870   0.00%             1623
          LSX         2     0.00%          2100   0.00%             1050

Signed-off-by: default avatarBibo Mao <maobibo@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
Signed-off-by: default avatarXianglai Li <lixianglai@loongson.cn>
parent 2ee1b090
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
endif
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
PERF_HAVE_JITDUMP := 1
HAVE_KVM_STAT_SUPPORT := 1

#
# Syscall table generation for perf
+2 −0
Original line number Diff line number Diff line
perf-y += header.o
perf-y += perf_regs.o

perf-$(CONFIG_DWARF)     += dwarf-regs.o
perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
+96 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Implementation of get_cpuid().
 *
 * Author: Nikita Shubin <n.shubin@yadro.com>
 *         Bibo Mao <maobibo@loongson.cn>
 *         Huacai Chen <chenhuacai@loongson.cn>
 */

#include <stdio.h>
#include <stdlib.h>
#include <api/fs/fs.h>
#include <errno.h>
#include "util/debug.h"
#include "util/header.h"

/*
 * Output example from /proc/cpuinfo
 *   CPU Family              : Loongson-64bit
 *   Model Name              : Loongson-3C5000
 *   CPU Revision            : 0x10
 *   FPU Revision            : 0x01
 */
#define CPUINFO_MODEL	"Model Name"
#define CPUINFO		"/proc/cpuinfo"

static char *_get_field(const char *line)
{
	char *line2, *nl;

	line2 = strrchr(line, ' ');
	if (!line2)
		return NULL;

	line2++;
	nl = strrchr(line, '\n');
	if (!nl)
		return NULL;

	return strndup(line2, nl - line2);
}

static char *_get_cpuid(void)
{
	unsigned long line_sz;
	char *line, *model, *cpuid;
	FILE *file;

	file = fopen(CPUINFO, "r");
	if (file == NULL)
		return NULL;

	line = model = cpuid = NULL;
	while (getline(&line, &line_sz, file) != -1) {
		if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
			continue;

		model = _get_field(line);
		if (!model)
			goto out_free;
		break;
	}

	if (model && (asprintf(&cpuid, "%s", model) < 0))
		cpuid = NULL;

out_free:
	fclose(file);
	free(model);
	return cpuid;
}

int get_cpuid(char *buffer, size_t sz)
{
	int ret = 0;
	char *cpuid = _get_cpuid();

	if (!cpuid)
		return EINVAL;

	if (sz < strlen(cpuid)) {
		ret = ENOBUFS;
		goto out_free;
	}

	scnprintf(buffer, sz, "%s", cpuid);

out_free:
	free(cpuid);
	return ret;
}

char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
{
	return _get_cpuid();
}
+139 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <memory.h>
#include "util/kvm-stat.h"
#include "util/parse-events.h"
#include "util/debug.h"
#include "util/evsel.h"
#include "util/evlist.h"
#include "util/pmus.h"

#define LOONGARCH_EXCEPTION_INT		0
#define LOONGARCH_EXCEPTION_PIL		1
#define LOONGARCH_EXCEPTION_PIS		2
#define LOONGARCH_EXCEPTION_PIF		3
#define LOONGARCH_EXCEPTION_PME		4
#define LOONGARCH_EXCEPTION_FPD		15
#define LOONGARCH_EXCEPTION_SXD		16
#define LOONGARCH_EXCEPTION_ASXD	17
#define LOONGARCH_EXCEPTION_GSPR	22
#define  LOONGARCH_EXCEPTION_CPUCFG	100
#define  LOONGARCH_EXCEPTION_CSR	101
#define  LOONGARCH_EXCEPTION_IOCSR	102
#define  LOONGARCH_EXCEPTION_IDLE	103
#define  LOONGARCH_EXCEPTION_OTHERS	104
#define LOONGARCH_EXCEPTION_HVC		23

#define loongarch_exception_type				\
	{LOONGARCH_EXCEPTION_INT,  "Interrupt" },		\
	{LOONGARCH_EXCEPTION_PIL,  "Mem Read" },		\
	{LOONGARCH_EXCEPTION_PIS,  "Mem Store" },		\
	{LOONGARCH_EXCEPTION_PIF,  "Inst Fetch" },		\
	{LOONGARCH_EXCEPTION_PME,  "Mem Modify" },		\
	{LOONGARCH_EXCEPTION_FPD,  "FPU" },			\
	{LOONGARCH_EXCEPTION_SXD,  "LSX" },			\
	{LOONGARCH_EXCEPTION_ASXD, "LASX" },			\
	{LOONGARCH_EXCEPTION_GSPR, "Privilege Error" },		\
	{LOONGARCH_EXCEPTION_HVC,  "Hypercall" },		\
	{LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" },		\
	{LOONGARCH_EXCEPTION_CSR,    "CSR" },			\
	{LOONGARCH_EXCEPTION_IOCSR,  "IOCSR" },			\
	{LOONGARCH_EXCEPTION_IDLE,   "Idle" },			\
	{LOONGARCH_EXCEPTION_OTHERS, "Others" }

define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);

const char *vcpu_id_str = "vcpu_id";
const char *kvm_exit_reason = "reason";
const char *kvm_entry_trace = "kvm:kvm_enter";
const char *kvm_reenter_trace = "kvm:kvm_reenter";
const char *kvm_exit_trace = "kvm:kvm_exit";
const char *kvm_events_tp[] = {
	"kvm:kvm_enter",
	"kvm:kvm_reenter",
	"kvm:kvm_exit",
	"kvm:kvm_exit_gspr",
	NULL,
};

static bool event_begin(struct evsel *evsel,
			struct perf_sample *sample, struct event_key *key)
{
	return exit_event_begin(evsel, sample, key);
}

static bool event_end(struct evsel *evsel,
		      struct perf_sample *sample __maybe_unused,
		      struct event_key *key __maybe_unused)
{
	/*
	 * LoongArch kvm is different with other architectures
	 *
	 * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
	 * kvm:kvm_exit event.
	 *   kvm:kvm_enter   means returning to vmm and then to guest
	 *   kvm:kvm_reenter means returning to guest immediately
	 */
	return evsel__name_is(evsel, kvm_entry_trace) || evsel__name_is(evsel, kvm_reenter_trace);
}

static void event_gspr_get_key(struct evsel *evsel,
			       struct perf_sample *sample, struct event_key *key)
{
	unsigned int insn;

	key->key = LOONGARCH_EXCEPTION_OTHERS;
	insn = evsel__intval(evsel, sample, "inst_word");

	switch (insn >> 24) {
	case 0:
		/* CPUCFG inst trap */
		if ((insn >> 10) == 0x1b)
			key->key = LOONGARCH_EXCEPTION_CPUCFG;
		break;
	case 4:
		/* CSR inst trap */
		key->key = LOONGARCH_EXCEPTION_CSR;
		break;
	case 6:
		/* IOCSR inst trap */
		if ((insn >> 15) == 0xc90)
			key->key = LOONGARCH_EXCEPTION_IOCSR;
		else if ((insn >> 15) == 0xc91)
			/* Idle inst trap */
			key->key = LOONGARCH_EXCEPTION_IDLE;
		break;
	default:
		key->key = LOONGARCH_EXCEPTION_OTHERS;
		break;
	}
}

static struct child_event_ops child_events[] = {
	{ .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
	{ NULL, NULL },
};

static struct kvm_events_ops exit_events = {
	.is_begin_event = event_begin,
	.is_end_event = event_end,
	.child_ops = child_events,
	.decode_key = exit_event_decode_key,
	.name = "VM-EXIT"
};

struct kvm_reg_events_ops kvm_reg_events_ops[] = {
	{ .name	= "vmexit", .ops = &exit_events, },
	{ NULL, NULL },
};

const char * const kvm_skip_events[] = {
	NULL,
};

int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
{
	kvm->exit_reasons_isa = "loongarch64";
	kvm->exit_reasons = loongarch_exit_reasons;
	return 0;
}