History

KVM: stats: Add fd-based API to read binary stats data

This commit defines the API for userspace and prepare the common functionalities to support per VM/VCPU binary stats data readings. The KVM stats now is only accessible by debugfs, which has some shortcomings this change series are supposed to fix: 1. The current debugfs stats solution in KVM could be disabled when kernel Lockdown mode is enabled, which is a potential rick for production. 2. The current debugfs stats solution in KVM is organized as "one stats per file", it is good for debugging, but not efficient for production. 3. The stats read/clear in current debugfs solution in KVM are protected by the global kvm_lock. Besides that, there are some other benefits with this change: 1. All KVM VM/VCPU stats can be read out in a bulk by one copy to userspace. 2. A schema is used to describe KVM statistics. From userspace's perspective, the KVM statistics are self-describing. 3. With the fd-based solution, a separate telemetry would be able to read KVM stats in a less privileged environment. 4. After the initial setup by reading in stats descriptors, a telemetry only needs to read the stats data itself, no more parsing or setup is needed.

Jing Zhang committed 5 years ago

Commit cb082bf1 parent 0193cc99 files · +307 −7Browse files

Unified · Split

9 files
  1. arch/arm64/kvm/Makefile +1 −1
  2. arch/mips/kvm/Makefile +1 −1
  3. arch/powerpc/kvm/Makefile +1 −1
  4. arch/s390/kvm/Makefile +2 −1
  5. arch/x86/kvm/Makefile +1 −1
  6. include/linux/kvm_host.h +80 −2
  7. include/linux/kvm_types.h +2 −0
  8. include/uapi/linux/kvm.h +73 −0
  9. virt/kvm/binary_stats.c +146 −0
arch/arm64/kvm/Makefile+1−1
@@ -11,7 +11,7 @@ obj-$(CONFIG_KVM) += kvm.o · 10 unmodified lines
1111 obj-$(CONFIG_KVM) += hyp/
1212
1313 kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \
14− $(KVM)/vfio.o $(KVM)/irqchip.o \
14+ $(KVM)/vfio.o $(KVM)/irqchip.o $(KVM)/binary_stats.o \
1515 arm.o mmu.o mmio.o psci.o perf.o hypercalls.o pvtime.o \
1616 inject_fault.o va_layout.o handle_exit.o \
1717 guest.o debug.o reset.o sys_regs.o \
arch/mips/kvm/Makefile+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 # Makefile for KVM support for MIPS
33 #
44
5−common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o eventfd.o)
5+common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o eventfd.o binary_stats.o)
66
77 EXTRA_CFLAGS += -Ivirt/kvm -Iarch/mips/kvm
88
arch/powerpc/kvm/Makefile+1−1
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 ccflags-y := -Ivirt/kvm -Iarch/powerpc/kvm
77 KVM := ../../../virt/kvm
88
9−common-objs-y = $(KVM)/kvm_main.o $(KVM)/eventfd.o
9+common-objs-y = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/binary_stats.o
1010 common-objs-$(CONFIG_KVM_VFIO) += $(KVM)/vfio.o
1111 common-objs-$(CONFIG_KVM_MMIO) += $(KVM)/coalesced_mmio.o
1212
arch/s390/kvm/Makefile+2−1
@@ -4,7 +4,8 @@ · 3 unmodified lines
44 # Copyright IBM Corp. 2008
55
66 KVM := ../../../virt/kvm
7−common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqchip.o $(KVM)/vfio.o
7+common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o \
8+ $(KVM)/irqchip.o $(KVM)/vfio.o $(KVM)/binary_stats.o
89
910 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
1011
arch/x86/kvm/Makefile+1−1
@@ -11,7 +11,7 @@ KVM := ../../../virt/kvm · 10 unmodified lines
1111
1212 kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \
1313 $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \
14− $(KVM)/dirty_ring.o
14+ $(KVM)/dirty_ring.o $(KVM)/binary_stats.o
1515 kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
1616
1717 kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
include/linux/kvm_host.h+80−2
@@ -1272,16 +1272,94 @@ struct kvm_stats_debugfs_item { · 1271 unmodified lines
12721272 int mode;
12731273 };
12741274
1275+struct _kvm_stats_desc {
1276+ struct kvm_stats_desc desc;
1277+ char name[KVM_STATS_NAME_SIZE];
1278+};
1279+
12751280 #define KVM_DBGFS_GET_MODE(dbgfs_item) \
12761281 ((dbgfs_item)->mode ? (dbgfs_item)->mode : 0644)
12771282
1278−#define VM_STAT(n, x, ...) \
1283+#define VM_STAT(n, x, ...) \
12791284 { n, offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__ }
1280−#define VCPU_STAT(n, x, ...) \
1285+#define VCPU_STAT(n, x, ...) \
12811286 { n, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__ }
12821287
1288+#define STATS_DESC_COMMON(type, unit, base, exp) \
1289+ .flags = type | unit | base | \
1290+ BUILD_BUG_ON_ZERO(type & ~KVM_STATS_TYPE_MASK) | \
1291+ BUILD_BUG_ON_ZERO(unit & ~KVM_STATS_UNIT_MASK) | \
1292+ BUILD_BUG_ON_ZERO(base & ~KVM_STATS_BASE_MASK), \
1293+ .exponent = exp, \
1294+ .size = 1
1295+
1296+#define VM_GENERIC_STATS_DESC(stat, type, unit, base, exp) \
1297+ { \
1298+ { \
1299+ STATS_DESC_COMMON(type, unit, base, exp), \
1300+ .offset = offsetof(struct kvm_vm_stat, generic.stat) \
1301+ }, \
1302+ .name = #stat, \
1303+ }
1304+#define VCPU_GENERIC_STATS_DESC(stat, type, unit, base, exp) \
1305+ { \
1306+ { \
1307+ STATS_DESC_COMMON(type, unit, base, exp), \
1308+ .offset = offsetof(struct kvm_vcpu_stat, generic.stat) \
1309+ }, \
1310+ .name = #stat, \
1311+ }
1312+#define VM_STATS_DESC(stat, type, unit, base, exp) \
1313+ { \
1314+ { \
1315+ STATS_DESC_COMMON(type, unit, base, exp), \
1316+ .offset = offsetof(struct kvm_vm_stat, stat) \
1317+ }, \
1318+ .name = #stat, \
1319+ }
1320+#define VCPU_STATS_DESC(stat, type, unit, base, exp) \
1321+ { \
1322+ { \
1323+ STATS_DESC_COMMON(type, unit, base, exp), \
1324+ .offset = offsetof(struct kvm_vcpu_stat, stat) \
1325+ }, \
1326+ .name = #stat, \
1327+ }
1328+/* SCOPE: VM, VM_GENERIC, VCPU, VCPU_GENERIC */
1329+#define STATS_DESC(SCOPE, stat, type, unit, base, exp) \
1330+ SCOPE##_STATS_DESC(stat, type, unit, base, exp)
1331+
1332+#define STATS_DESC_CUMULATIVE(SCOPE, name, unit, base, exponent) \
1333+ STATS_DESC(SCOPE, name, KVM_STATS_TYPE_CUMULATIVE, unit, base, exponent)
1334+#define STATS_DESC_INSTANT(SCOPE, name, unit, base, exponent) \
1335+ STATS_DESC(SCOPE, name, KVM_STATS_TYPE_INSTANT, unit, base, exponent)
1336+#define STATS_DESC_PEAK(SCOPE, name, unit, base, exponent) \
1337+ STATS_DESC(SCOPE, name, KVM_STATS_TYPE_PEAK, unit, base, exponent)
1338+
1339+/* Cumulative counter, read/write */
1340+#define STATS_DESC_COUNTER(SCOPE, name) \
1341+ STATS_DESC_CUMULATIVE(SCOPE, name, KVM_STATS_UNIT_NONE, \
1342+ KVM_STATS_BASE_POW10, 0)
1343+/* Instantaneous counter, read only */
1344+#define STATS_DESC_ICOUNTER(SCOPE, name) \
1345+ STATS_DESC_INSTANT(SCOPE, name, KVM_STATS_UNIT_NONE, \
1346+ KVM_STATS_BASE_POW10, 0)
1347+/* Peak counter, read/write */
1348+#define STATS_DESC_PCOUNTER(SCOPE, name) \
1349+ STATS_DESC_PEAK(SCOPE, name, KVM_STATS_UNIT_NONE, \
1350+ KVM_STATS_BASE_POW10, 0)
1351+
1352+/* Cumulative time in nanosecond */
1353+#define STATS_DESC_TIME_NSEC(SCOPE, name) \
1354+ STATS_DESC_CUMULATIVE(SCOPE, name, KVM_STATS_UNIT_SECONDS, \
1355+ KVM_STATS_BASE_POW10, -9)
1356+
12831357 extern struct kvm_stats_debugfs_item debugfs_entries[];
12841358 extern struct dentry *kvm_debugfs_dir;
1359+ssize_t kvm_stats_read(char *id, const struct kvm_stats_header *header,
1360+ const struct _kvm_stats_desc *desc,
1361+ void *stats, size_t size_stats,
1362+ char __user *user_buffer, size_t size, loff_t *offset);
12851363
12861364 #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
12871365 static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
include/linux/kvm_types.h+2−0
@@ -89,4 +89,6 @@ struct kvm_vcpu_stat_generic { · 88 unmodified lines
8989 u64 halt_poll_fail_ns;
9090 };
9191
92+#define KVM_STATS_NAME_SIZE 48
93+
9294 #endif /* __KVM_TYPES_H__ */
include/uapi/linux/kvm.h+73−0
@@ -1087,6 +1087,7 @@ struct kvm_ppc_resize_hpt { · 1086 unmodified lines
10871087 #define KVM_CAP_SREGS2 200
10881088 #define KVM_CAP_EXIT_HYPERCALL 201
10891089 #define KVM_CAP_PPC_RPT_INVALIDATE 202
1090+#define KVM_CAP_BINARY_STATS_FD 203
10901091
10911092 #ifdef KVM_CAP_IRQ_ROUTING
10921093
@@ -1906,4 +1907,76 @@ struct kvm_dirty_gfn { · 813 unmodified lines
19061907 #define KVM_BUS_LOCK_DETECTION_OFF (1 << 0)
19071908 #define KVM_BUS_LOCK_DETECTION_EXIT (1 << 1)
19081909
1910+/**
1911+ * struct kvm_stats_header - Header of per vm/vcpu binary statistics data.
1912+ * @flags: Some extra information for header, always 0 for now.
1913+ * @name_size: The size in bytes of the memory which contains statistics
1914+ * name string including trailing '\0'. The memory is allocated
1915+ * at the send of statistics descriptor.
1916+ * @num_desc: The number of statistics the vm or vcpu has.
1917+ * @id_offset: The offset of the vm/vcpu stats' id string in the file pointed
1918+ * by vm/vcpu stats fd.
1919+ * @desc_offset: The offset of the vm/vcpu stats' descriptor block in the file
1920+ * pointd by vm/vcpu stats fd.
1921+ * @data_offset: The offset of the vm/vcpu stats' data block in the file
1922+ * pointed by vm/vcpu stats fd.
1923+ *
1924+ * This is the header userspace needs to read from stats fd before any other
1925+ * readings. It is used by userspace to discover all the information about the
1926+ * vm/vcpu's binary statistics.
1927+ * Userspace reads this header from the start of the vm/vcpu's stats fd.
1928+ */
1929+struct kvm_stats_header {
1930+ __u32 flags;
1931+ __u32 name_size;
1932+ __u32 num_desc;
1933+ __u32 id_offset;
1934+ __u32 desc_offset;
1935+ __u32 data_offset;
1936+};
1937+
1938+#define KVM_STATS_TYPE_SHIFT 0
1939+#define KVM_STATS_TYPE_MASK (0xF << KVM_STATS_TYPE_SHIFT)
1940+#define KVM_STATS_TYPE_CUMULATIVE (0x0 << KVM_STATS_TYPE_SHIFT)
1941+#define KVM_STATS_TYPE_INSTANT (0x1 << KVM_STATS_TYPE_SHIFT)
1942+#define KVM_STATS_TYPE_PEAK (0x2 << KVM_STATS_TYPE_SHIFT)
1943+#define KVM_STATS_TYPE_MAX KVM_STATS_TYPE_PEAK
1944+
1945+#define KVM_STATS_UNIT_SHIFT 4
1946+#define KVM_STATS_UNIT_MASK (0xF << KVM_STATS_UNIT_SHIFT)
1947+#define KVM_STATS_UNIT_NONE (0x0 << KVM_STATS_UNIT_SHIFT)
1948+#define KVM_STATS_UNIT_BYTES (0x1 << KVM_STATS_UNIT_SHIFT)
1949+#define KVM_STATS_UNIT_SECONDS (0x2 << KVM_STATS_UNIT_SHIFT)
1950+#define KVM_STATS_UNIT_CYCLES (0x3 << KVM_STATS_UNIT_SHIFT)
1951+#define KVM_STATS_UNIT_MAX KVM_STATS_UNIT_CYCLES
1952+
1953+#define KVM_STATS_BASE_SHIFT 8
1954+#define KVM_STATS_BASE_MASK (0xF << KVM_STATS_BASE_SHIFT)
1955+#define KVM_STATS_BASE_POW10 (0x0 << KVM_STATS_BASE_SHIFT)
1956+#define KVM_STATS_BASE_POW2 (0x1 << KVM_STATS_BASE_SHIFT)
1957+#define KVM_STATS_BASE_MAX KVM_STATS_BASE_POW2
1958+
1959+/**
1960+ * struct kvm_stats_desc - Descriptor of a KVM statistics.
1961+ * @flags: Annotations of the stats, like type, unit, etc.
1962+ * @exponent: Used together with @flags to determine the unit.
1963+ * @size: The number of data items for this stats.
1964+ * Every data item is of type __u64.
1965+ * @offset: The offset of the stats to the start of stat structure in
1966+ * struture kvm or kvm_vcpu.
1967+ * @unused: Unused field for future usage. Always 0 for now.
1968+ * @name: The name string for the stats. Its size is indicated by the
1969+ * &kvm_stats_header->name_size.
1970+ */
1971+struct kvm_stats_desc {
1972+ __u32 flags;
1973+ __s16 exponent;
1974+ __u16 size;
1975+ __u32 offset;
1976+ __u32 unused;
1977+ char name[];
1978+};
1979+
1980+#define KVM_GET_STATS_FD _IO(KVMIO, 0xce)
1981+
19091982 #endif /* __LINUX_KVM_H */
virt/kvm/binary_stats.c+146−0
@@ -0,0 +1,146 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * KVM binary statistics interface implementation
4+ *
5+ * Copyright 2021 Google LLC
6+ */
7+
8+#include <linux/kvm_host.h>
9+#include <linux/kvm.h>
10+#include <linux/errno.h>
11+#include <linux/uaccess.h>
12+
13+/**
14+ * kvm_stats_read() - Common function to read from the binary statistics
15+ * file descriptor.
16+ *
17+ * @id: identification string of the stats
18+ * @header: stats header for a vm or a vcpu
19+ * @desc: start address of an array of stats descriptors for a vm or a vcpu
20+ * @stats: start address of stats data block for a vm or a vcpu
21+ * @size_stats: the size of stats data block pointed by @stats
22+ * @user_buffer: start address of userspace buffer
23+ * @size: requested read size from userspace
24+ * @offset: the start position from which the content will be read for the
25+ * corresponding vm or vcp file descriptor
26+ *
27+ * The file content of a vm/vcpu file descriptor is now defined as below:
28+ * +-------------+
29+ * | Header |
30+ * +-------------+
31+ * | id string |
32+ * +-------------+
33+ * | Descriptors |
34+ * +-------------+
35+ * | Stats Data |
36+ * +-------------+
37+ * Although this function allows userspace to read any amount of data (as long
38+ * as in the limit) from any position, the typical usage would follow below
39+ * steps:
40+ * 1. Read header from offset 0. Get the offset of descriptors and stats data
41+ * and some other necessary information. This is a one-time work for the
42+ * lifecycle of the corresponding vm/vcpu stats fd.
43+ * 2. Read id string from its offset. This is a one-time work for the lifecycle
44+ * of the corresponding vm/vcpu stats fd.
45+ * 3. Read descriptors from its offset and discover all the stats by parsing
46+ * descriptors. This is a one-time work for the lifecycle of the
47+ * corresponding vm/vcpu stats fd.
48+ * 4. Periodically read stats data from its offset using pread.
49+ *
50+ * Return: the number of bytes that has been successfully read
51+ */
52+ssize_t kvm_stats_read(char *id, const struct kvm_stats_header *header,
53+ const struct _kvm_stats_desc *desc,
54+ void *stats, size_t size_stats,
55+ char __user *user_buffer, size_t size, loff_t *offset)
56+{
57+ ssize_t len;
58+ ssize_t copylen;
59+ ssize_t remain = size;
60+ size_t size_desc;
61+ size_t size_header;
62+ void *src;
63+ loff_t pos = *offset;
64+ char __user *dest = user_buffer;
65+
66+ size_header = sizeof(*header);
67+ size_desc = header->num_desc * sizeof(*desc);
68+
69+ len = KVM_STATS_NAME_SIZE + size_header + size_desc + size_stats - pos;
70+ len = min(len, remain);
71+ if (len <= 0)
72+ return 0;
73+ remain = len;
74+
75+ /*
76+ * Copy kvm stats header.
77+ * The header is the first block of content userspace usually read out.
78+ * The pos is 0 and the copylen and remain would be the size of header.
79+ * The copy of the header would be skipped if offset is larger than the
80+ * size of header. That usually happens when userspace reads stats
81+ * descriptors and stats data.
82+ */
83+ copylen = size_header - pos;
84+ copylen = min(copylen, remain);
85+ if (copylen > 0) {
86+ src = (void *)header + pos;
87+ if (copy_to_user(dest, src, copylen))
88+ return -EFAULT;
89+ remain -= copylen;
90+ pos += copylen;
91+ dest += copylen;
92+ }
93+
94+ /*
95+ * Copy kvm stats header id string.
96+ * The id string is unique for every vm/vcpu, which is stored in kvm
97+ * and kvm_vcpu structure.
98+ * The id string is part of the stat header from the perspective of
99+ * userspace, it is usually read out together with previous constant
100+ * header part and could be skipped for later descriptors and stats
101+ * data readings.
102+ */
103+ copylen = header->id_offset + KVM_STATS_NAME_SIZE - pos;
104+ copylen = min(copylen, remain);
105+ if (copylen > 0) {
106+ src = id + pos - header->id_offset;
107+ if (copy_to_user(dest, src, copylen))
108+ return -EFAULT;
109+ remain -= copylen;
110+ pos += copylen;
111+ dest += copylen;
112+ }
113+
114+ /*
115+ * Copy kvm stats descriptors.
116+ * The descriptors copy would be skipped in the typical case that
117+ * userspace periodically read stats data, since the pos would be
118+ * greater than the end address of descriptors
119+ * (header->header.desc_offset + size_desc) causing copylen <= 0.
120+ */
121+ copylen = header->desc_offset + size_desc - pos;
122+ copylen = min(copylen, remain);
123+ if (copylen > 0) {
124+ src = (void *)desc + pos - header->desc_offset;
125+ if (copy_to_user(dest, src, copylen))
126+ return -EFAULT;
127+ remain -= copylen;
128+ pos += copylen;
129+ dest += copylen;
130+ }
131+
132+ /* Copy kvm stats values */
133+ copylen = header->data_offset + size_stats - pos;
134+ copylen = min(copylen, remain);
135+ if (copylen > 0) {
136+ src = stats + pos - header->data_offset;
137+ if (copy_to_user(dest, src, copylen))
138+ return -EFAULT;
139+ remain -= copylen;
140+ pos += copylen;
141+ dest += copylen;
142+ }
143+
144+ *offset = pos;
145+ return len;
146+}