1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * kvm_binary_stats_test
4 *
5 * Copyright (C) 2021, Google LLC.
6 *
7 * Test the fd-based interface for KVM statistics.
8 */
9
10#define _GNU_SOURCE /* for program_invocation_short_name */
11#include <fcntl.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <errno.h>
16
17#include "test_util.h"
18
19#include "kvm_util.h"
20#include "asm/kvm.h"
21#include "linux/kvm.h"
22#include "kselftest.h"
23
24static void stats_test(int stats_fd)
25{
26 ssize_t ret;
27 int i;
28 size_t size_desc;
29 size_t size_data = 0;
30 struct kvm_stats_header header;
31 char *id;
32 struct kvm_stats_desc *stats_desc;
33 u64 *stats_data;
34 struct kvm_stats_desc *pdesc;
35 u32 type, unit, base;
36
37 /* Read kvm stats header */
38 read_stats_header(stats_fd, &header);
39
40 size_desc = get_stats_descriptor_size(&header);
41
42 /* Read kvm stats id string */
43 id = malloc(header.name_size);
44 TEST_ASSERT(id, "Allocate memory for id string");
45
46 ret = pread(stats_fd, id, header.name_size, sizeof(header));
47 TEST_ASSERT(ret == header.name_size,
48 "Expected header size '%u', read '%lu' bytes",
49 header.name_size, ret);
50
51 /* Check id string, that should start with "kvm" */
52 TEST_ASSERT(!strncmp(id, "kvm", 3) && strlen(id) < header.name_size,
53 "Invalid KVM stats type, id: %s", id);
54
55 /* Sanity check for other fields in header */
56 if (header.num_desc == 0) {
57 ksft_print_msg("No KVM stats defined!\n");
58 return;
59 }
60 /*
61 * The descriptor and data offsets must be valid, they must not overlap
62 * the header, and the descriptor and data blocks must not overlap each
63 * other. Note, the data block is rechecked after its size is known.
64 */
65 TEST_ASSERT(header.desc_offset && header.desc_offset >= sizeof(header) &&
66 header.data_offset && header.data_offset >= sizeof(header),
67 "Invalid offset fields in header");
68
69 TEST_ASSERT(header.desc_offset > header.data_offset ||
70 (header.desc_offset + size_desc * header.num_desc <= header.data_offset),
71 "Descriptor block is overlapped with data block");
72
73 /* Read kvm stats descriptors */
74 stats_desc = read_stats_descriptors(stats_fd, &header);
75
76 /* Sanity check for fields in descriptors */
77 for (i = 0; i < header.num_desc; ++i) {
78 pdesc = get_stats_descriptor(stats_desc, i, &header);
79 type = pdesc->flags & KVM_STATS_TYPE_MASK;
80 unit = pdesc->flags & KVM_STATS_UNIT_MASK;
81 base = pdesc->flags & KVM_STATS_BASE_MASK;
82
83 /* Check name string */
84 TEST_ASSERT(strlen(pdesc->name) < header.name_size,
85 "KVM stats name (index: %d) too long", i);
86
87 /* Check type,unit,base boundaries */
88 TEST_ASSERT(type <= KVM_STATS_TYPE_MAX,
89 "Unknown KVM stats (%s) type: %u", pdesc->name, type);
90 TEST_ASSERT(unit <= KVM_STATS_UNIT_MAX,
91 "Unknown KVM stats (%s) unit: %u", pdesc->name, unit);
92 TEST_ASSERT(base <= KVM_STATS_BASE_MAX,
93 "Unknown KVM stats (%s) base: %u", pdesc->name, base);
94
95 /*
96 * Check exponent for stats unit
97 * Exponent for counter should be greater than or equal to 0
98 * Exponent for unit bytes should be greater than or equal to 0
99 * Exponent for unit seconds should be less than or equal to 0
100 * Exponent for unit clock cycles should be greater than or
101 * equal to 0
102 * Exponent for unit boolean should be 0
103 */
104 switch (pdesc->flags & KVM_STATS_UNIT_MASK) {
105 case KVM_STATS_UNIT_NONE:
106 case KVM_STATS_UNIT_BYTES:
107 case KVM_STATS_UNIT_CYCLES:
108 TEST_ASSERT(pdesc->exponent >= 0,
109 "Unsupported KVM stats (%s) exponent: %i",
110 pdesc->name, pdesc->exponent);
111 break;
112 case KVM_STATS_UNIT_SECONDS:
113 TEST_ASSERT(pdesc->exponent <= 0,
114 "Unsupported KVM stats (%s) exponent: %i",
115 pdesc->name, pdesc->exponent);
116 break;
117 case KVM_STATS_UNIT_BOOLEAN:
118 TEST_ASSERT(pdesc->exponent == 0,
119 "Unsupported KVM stats (%s) exponent: %d",
120 pdesc->name, pdesc->exponent);
121 break;
122 }
123
124 /* Check size field, which should not be zero */
125 TEST_ASSERT(pdesc->size,
126 "KVM descriptor(%s) with size of 0", pdesc->name);
127 /* Check bucket_size field */
128 switch (pdesc->flags & KVM_STATS_TYPE_MASK) {
129 case KVM_STATS_TYPE_LINEAR_HIST:
130 TEST_ASSERT(pdesc->bucket_size,
131 "Bucket size of Linear Histogram stats (%s) is zero",
132 pdesc->name);
133 break;
134 default:
135 TEST_ASSERT(!pdesc->bucket_size,
136 "Bucket size of stats (%s) is not zero",
137 pdesc->name);
138 }
139 size_data = max(size_data, pdesc->offset + pdesc->size * sizeof(*stats_data));
140 }
141
142 /*
143 * Now that the size of the data block is known, verify the data block
144 * doesn't overlap the descriptor block.
145 */
146 TEST_ASSERT(header.data_offset >= header.desc_offset ||
147 header.data_offset + size_data <= header.desc_offset,
148 "Data block is overlapped with Descriptor block");
149
150 /* Check validity of all stats data size */
151 TEST_ASSERT(size_data >= header.num_desc * sizeof(*stats_data),
152 "Data size is not correct");
153
154 /* Allocate memory for stats data */
155 stats_data = malloc(size_data);
156 TEST_ASSERT(stats_data, "Allocate memory for stats data");
157 /* Read kvm stats data as a bulk */
158 ret = pread(stats_fd, stats_data, size_data, header.data_offset);
159 TEST_ASSERT(ret == size_data, "Read KVM stats data");
160 /* Read kvm stats data one by one */
161 for (i = 0; i < header.num_desc; ++i) {
162 pdesc = get_stats_descriptor(stats_desc, i, &header);
163 read_stat_data(stats_fd, &header, pdesc, stats_data,
164 pdesc->size);
165 }
166
167 free(stats_data);
168 free(stats_desc);
169 free(id);
170
171 close(stats_fd);
172 TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed");
173}
174
175#define DEFAULT_NUM_VM 4
176#define DEFAULT_NUM_VCPU 4
177
178/*
179 * Usage: kvm_bin_form_stats [#vm] [#vcpu]
180 * The first parameter #vm set the number of VMs being created.
181 * The second parameter #vcpu set the number of VCPUs being created.
182 * By default, DEFAULT_NUM_VM VM and DEFAULT_NUM_VCPU VCPU for the VM would be
183 * created for testing.
184 */
185
186int main(int argc, char *argv[])
187{
188 int vm_stats_fds, *vcpu_stats_fds;
189 int i, j;
190 struct kvm_vcpu **vcpus;
191 struct kvm_vm **vms;
192 int max_vm = DEFAULT_NUM_VM;
193 int max_vcpu = DEFAULT_NUM_VCPU;
194
195 /* Get the number of VMs and VCPUs that would be created for testing. */
196 if (argc > 1) {
197 max_vm = strtol(argv[1], NULL, 0);
198 if (max_vm <= 0)
199 max_vm = DEFAULT_NUM_VM;
200 }
201 if (argc > 2) {
202 max_vcpu = strtol(argv[2], NULL, 0);
203 if (max_vcpu <= 0)
204 max_vcpu = DEFAULT_NUM_VCPU;
205 }
206
207 ksft_print_header();
208
209 /* Check the extension for binary stats */
210 TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD));
211
212 ksft_set_plan(max_vm);
213
214 /* Create VMs and VCPUs */
215 vms = malloc(sizeof(vms[0]) * max_vm);
216 TEST_ASSERT(vms, "Allocate memory for storing VM pointers");
217
218 vcpus = malloc(sizeof(struct kvm_vcpu *) * max_vm * max_vcpu);
219 TEST_ASSERT(vcpus, "Allocate memory for storing vCPU pointers");
220
221 /*
222 * Not per-VM as the array is populated, used, and invalidated within a
223 * single for-loop iteration.
224 */
225 vcpu_stats_fds = calloc(max_vm, sizeof(*vcpu_stats_fds));
226 TEST_ASSERT(vcpu_stats_fds, "Allocate memory for VM stats fds");
227
228 for (i = 0; i < max_vm; ++i) {
229 vms[i] = vm_create_barebones();
230 for (j = 0; j < max_vcpu; ++j)
231 vcpus[i * max_vcpu + j] = __vm_vcpu_add(vms[i], j);
232 }
233
234 /*
235 * Check stats read for every VM and vCPU, with a variety of flavors.
236 * Note, stats_test() closes the passed in stats fd.
237 */
238 for (i = 0; i < max_vm; ++i) {
239 /*
240 * Verify that creating multiple userspace references to a
241 * single stats file works and doesn't cause explosions.
242 */
243 vm_stats_fds = vm_get_stats_fd(vms[i]);
244 stats_test(stats_fd: dup(vm_stats_fds));
245
246 /* Verify userspace can instantiate multiple stats files. */
247 stats_test(stats_fd: vm_get_stats_fd(vms[i]));
248
249 for (j = 0; j < max_vcpu; ++j) {
250 vcpu_stats_fds[j] = vcpu_get_stats_fd(vcpus[i * max_vcpu + j]);
251 stats_test(stats_fd: dup(vcpu_stats_fds[j]));
252 stats_test(stats_fd: vcpu_get_stats_fd(vcpus[i * max_vcpu + j]));
253 }
254
255 /*
256 * Close the VM fd and redo the stats tests. KVM should gift a
257 * reference (to the VM) to each stats fd, i.e. stats should
258 * still be accessible even after userspace has put its last
259 * _direct_ reference to the VM.
260 */
261 kvm_vm_free(vms[i]);
262
263 stats_test(stats_fd: vm_stats_fds);
264 for (j = 0; j < max_vcpu; ++j)
265 stats_test(stats_fd: vcpu_stats_fds[j]);
266
267 ksft_test_result_pass("vm%i\n", i);
268 }
269
270 free(vms);
271 free(vcpus);
272 free(vcpu_stats_fds);
273
274 ksft_finished(); /* Print results and exit() accordingly */
275}
276

source code of linux/tools/testing/selftests/kvm/kvm_binary_stats_test.c