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 
23 static void stats_test(int stats_fd)
24 {
25 	ssize_t ret;
26 	int i;
27 	size_t size_desc;
28 	size_t size_data = 0;
29 	struct kvm_stats_header header;
30 	char *id;
31 	struct kvm_stats_desc *stats_desc;
32 	u64 *stats_data;
33 	struct kvm_stats_desc *pdesc;
34 
35 	/* Read kvm stats header */
36 	read_stats_header(stats_fd, &header);
37 
38 	size_desc = get_stats_descriptor_size(&header);
39 
40 	/* Read kvm stats id string */
41 	id = malloc(header.name_size);
42 	TEST_ASSERT(id, "Allocate memory for id string");
43 
44 	ret = read(stats_fd, id, header.name_size);
45 	TEST_ASSERT(ret == header.name_size, "Read id string");
46 
47 	/* Check id string, that should start with "kvm" */
48 	TEST_ASSERT(!strncmp(id, "kvm", 3) && strlen(id) < header.name_size,
49 		    "Invalid KVM stats type, id: %s", id);
50 
51 	/* Sanity check for other fields in header */
52 	if (header.num_desc == 0) {
53 		printf("No KVM stats defined!");
54 		return;
55 	}
56 	/*
57 	 * The descriptor and data offsets must be valid, they must not overlap
58 	 * the header, and the descriptor and data blocks must not overlap each
59 	 * other.  Note, the data block is rechecked after its size is known.
60 	 */
61 	TEST_ASSERT(header.desc_offset && header.desc_offset >= sizeof(header) &&
62 		    header.data_offset && header.data_offset >= sizeof(header),
63 		    "Invalid offset fields in header");
64 
65 	TEST_ASSERT(header.desc_offset > header.data_offset ||
66 		    (header.desc_offset + size_desc * header.num_desc <= header.data_offset),
67 		    "Descriptor block is overlapped with data block");
68 
69 	/* Read kvm stats descriptors */
70 	stats_desc = read_stats_descriptors(stats_fd, &header);
71 
72 	/* Sanity check for fields in descriptors */
73 	for (i = 0; i < header.num_desc; ++i) {
74 		pdesc = get_stats_descriptor(stats_desc, i, &header);
75 
76 		/* Check type,unit,base boundaries */
77 		TEST_ASSERT((pdesc->flags & KVM_STATS_TYPE_MASK) <= KVM_STATS_TYPE_MAX,
78 			    "Unknown KVM stats type");
79 		TEST_ASSERT((pdesc->flags & KVM_STATS_UNIT_MASK) <= KVM_STATS_UNIT_MAX,
80 			    "Unknown KVM stats unit");
81 		TEST_ASSERT((pdesc->flags & KVM_STATS_BASE_MASK) <= KVM_STATS_BASE_MAX,
82 			    "Unknown KVM stats base");
83 
84 		/*
85 		 * Check exponent for stats unit
86 		 * Exponent for counter should be greater than or equal to 0
87 		 * Exponent for unit bytes should be greater than or equal to 0
88 		 * Exponent for unit seconds should be less than or equal to 0
89 		 * Exponent for unit clock cycles should be greater than or
90 		 * equal to 0
91 		 */
92 		switch (pdesc->flags & KVM_STATS_UNIT_MASK) {
93 		case KVM_STATS_UNIT_NONE:
94 		case KVM_STATS_UNIT_BYTES:
95 		case KVM_STATS_UNIT_CYCLES:
96 			TEST_ASSERT(pdesc->exponent >= 0, "Unsupported KVM stats unit");
97 			break;
98 		case KVM_STATS_UNIT_SECONDS:
99 			TEST_ASSERT(pdesc->exponent <= 0, "Unsupported KVM stats unit");
100 			break;
101 		}
102 		/* Check name string */
103 		TEST_ASSERT(strlen(pdesc->name) < header.name_size,
104 			    "KVM stats name(%s) too long", pdesc->name);
105 		/* Check size field, which should not be zero */
106 		TEST_ASSERT(pdesc->size,
107 			    "KVM descriptor(%s) with size of 0", pdesc->name);
108 		/* Check bucket_size field */
109 		switch (pdesc->flags & KVM_STATS_TYPE_MASK) {
110 		case KVM_STATS_TYPE_LINEAR_HIST:
111 			TEST_ASSERT(pdesc->bucket_size,
112 				    "Bucket size of Linear Histogram stats (%s) is zero",
113 				    pdesc->name);
114 			break;
115 		default:
116 			TEST_ASSERT(!pdesc->bucket_size,
117 				    "Bucket size of stats (%s) is not zero",
118 				    pdesc->name);
119 		}
120 		size_data += pdesc->size * sizeof(*stats_data);
121 	}
122 
123 	/*
124 	 * Now that the size of the data block is known, verify the data block
125 	 * doesn't overlap the descriptor block.
126 	 */
127 	TEST_ASSERT(header.data_offset >= header.desc_offset ||
128 		    header.data_offset + size_data <= header.desc_offset,
129 		    "Data block is overlapped with Descriptor block");
130 
131 	/* Check validity of all stats data size */
132 	TEST_ASSERT(size_data >= header.num_desc * sizeof(*stats_data),
133 		    "Data size is not correct");
134 
135 	/* Check stats offset */
136 	for (i = 0; i < header.num_desc; ++i) {
137 		pdesc = get_stats_descriptor(stats_desc, i, &header);
138 		TEST_ASSERT(pdesc->offset < size_data,
139 			    "Invalid offset (%u) for stats: %s",
140 			    pdesc->offset, pdesc->name);
141 	}
142 
143 	/* Allocate memory for stats data */
144 	stats_data = malloc(size_data);
145 	TEST_ASSERT(stats_data, "Allocate memory for stats data");
146 	/* Read kvm stats data as a bulk */
147 	ret = pread(stats_fd, stats_data, size_data, header.data_offset);
148 	TEST_ASSERT(ret == size_data, "Read KVM stats data");
149 	/* Read kvm stats data one by one */
150 	size_data = 0;
151 	for (i = 0; i < header.num_desc; ++i) {
152 		pdesc = get_stats_descriptor(stats_desc, i, &header);
153 		ret = pread(stats_fd, stats_data,
154 				pdesc->size * sizeof(*stats_data),
155 				header.data_offset + size_data);
156 		TEST_ASSERT(ret == pdesc->size * sizeof(*stats_data),
157 				"Read data of KVM stats: %s", pdesc->name);
158 		size_data += pdesc->size * sizeof(*stats_data);
159 	}
160 
161 	free(stats_data);
162 	free(stats_desc);
163 	free(id);
164 }
165 
166 
167 static void vm_stats_test(struct kvm_vm *vm)
168 {
169 	int stats_fd = vm_get_stats_fd(vm);
170 
171 	stats_test(stats_fd);
172 	close(stats_fd);
173 	TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed");
174 }
175 
176 static void vcpu_stats_test(struct kvm_vcpu *vcpu)
177 {
178 	int stats_fd = vcpu_get_stats_fd(vcpu);
179 
180 	stats_test(stats_fd);
181 	close(stats_fd);
182 	TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed");
183 }
184 
185 #define DEFAULT_NUM_VM		4
186 #define DEFAULT_NUM_VCPU	4
187 
188 /*
189  * Usage: kvm_bin_form_stats [#vm] [#vcpu]
190  * The first parameter #vm set the number of VMs being created.
191  * The second parameter #vcpu set the number of VCPUs being created.
192  * By default, DEFAULT_NUM_VM VM and DEFAULT_NUM_VCPU VCPU for the VM would be
193  * created for testing.
194  */
195 
196 int main(int argc, char *argv[])
197 {
198 	int i, j;
199 	struct kvm_vcpu **vcpus;
200 	struct kvm_vm **vms;
201 	int max_vm = DEFAULT_NUM_VM;
202 	int max_vcpu = DEFAULT_NUM_VCPU;
203 
204 	/* Get the number of VMs and VCPUs that would be created for testing. */
205 	if (argc > 1) {
206 		max_vm = strtol(argv[1], NULL, 0);
207 		if (max_vm <= 0)
208 			max_vm = DEFAULT_NUM_VM;
209 	}
210 	if (argc > 2) {
211 		max_vcpu = strtol(argv[2], NULL, 0);
212 		if (max_vcpu <= 0)
213 			max_vcpu = DEFAULT_NUM_VCPU;
214 	}
215 
216 	/* Check the extension for binary stats */
217 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD));
218 
219 	/* Create VMs and VCPUs */
220 	vms = malloc(sizeof(vms[0]) * max_vm);
221 	TEST_ASSERT(vms, "Allocate memory for storing VM pointers");
222 
223 	vcpus = malloc(sizeof(struct kvm_vcpu *) * max_vm * max_vcpu);
224 	TEST_ASSERT(vcpus, "Allocate memory for storing vCPU pointers");
225 
226 	for (i = 0; i < max_vm; ++i) {
227 		vms[i] = vm_create_barebones();
228 		for (j = 0; j < max_vcpu; ++j)
229 			vcpus[i * max_vcpu + j] = __vm_vcpu_add(vms[i], j);
230 	}
231 
232 	/* Check stats read for every VM and VCPU */
233 	for (i = 0; i < max_vm; ++i) {
234 		vm_stats_test(vms[i]);
235 		for (j = 0; j < max_vcpu; ++j)
236 			vcpu_stats_test(vcpus[i * max_vcpu + j]);
237 	}
238 
239 	for (i = 0; i < max_vm; ++i)
240 		kvm_vm_free(vms[i]);
241 	free(vms);
242 	return 0;
243 }
244