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