Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * kvm_binary_stats_test
0004  *
0005  * Copyright (C) 2021, Google LLC.
0006  *
0007  * Test the fd-based interface for KVM statistics.
0008  */
0009 
0010 #define _GNU_SOURCE /* for program_invocation_short_name */
0011 #include <fcntl.h>
0012 #include <stdio.h>
0013 #include <stdlib.h>
0014 #include <string.h>
0015 #include <errno.h>
0016 
0017 #include "test_util.h"
0018 
0019 #include "kvm_util.h"
0020 #include "asm/kvm.h"
0021 #include "linux/kvm.h"
0022 
0023 static void stats_test(int stats_fd)
0024 {
0025     ssize_t ret;
0026     int i;
0027     size_t size_desc;
0028     size_t size_data = 0;
0029     struct kvm_stats_header header;
0030     char *id;
0031     struct kvm_stats_desc *stats_desc;
0032     u64 *stats_data;
0033     struct kvm_stats_desc *pdesc;
0034     u32 type, unit, base;
0035 
0036     /* Read kvm stats header */
0037     read_stats_header(stats_fd, &header);
0038 
0039     size_desc = get_stats_descriptor_size(&header);
0040 
0041     /* Read kvm stats id string */
0042     id = malloc(header.name_size);
0043     TEST_ASSERT(id, "Allocate memory for id string");
0044 
0045     ret = read(stats_fd, id, header.name_size);
0046     TEST_ASSERT(ret == header.name_size, "Read id string");
0047 
0048     /* Check id string, that should start with "kvm" */
0049     TEST_ASSERT(!strncmp(id, "kvm", 3) && strlen(id) < header.name_size,
0050             "Invalid KVM stats type, id: %s", id);
0051 
0052     /* Sanity check for other fields in header */
0053     if (header.num_desc == 0) {
0054         printf("No KVM stats defined!");
0055         return;
0056     }
0057     /*
0058      * The descriptor and data offsets must be valid, they must not overlap
0059      * the header, and the descriptor and data blocks must not overlap each
0060      * other.  Note, the data block is rechecked after its size is known.
0061      */
0062     TEST_ASSERT(header.desc_offset && header.desc_offset >= sizeof(header) &&
0063             header.data_offset && header.data_offset >= sizeof(header),
0064             "Invalid offset fields in header");
0065 
0066     TEST_ASSERT(header.desc_offset > header.data_offset ||
0067             (header.desc_offset + size_desc * header.num_desc <= header.data_offset),
0068             "Descriptor block is overlapped with data block");
0069 
0070     /* Read kvm stats descriptors */
0071     stats_desc = read_stats_descriptors(stats_fd, &header);
0072 
0073     /* Sanity check for fields in descriptors */
0074     for (i = 0; i < header.num_desc; ++i) {
0075         pdesc = get_stats_descriptor(stats_desc, i, &header);
0076         type = pdesc->flags & KVM_STATS_TYPE_MASK;
0077         unit = pdesc->flags & KVM_STATS_UNIT_MASK;
0078         base = pdesc->flags & KVM_STATS_BASE_MASK;
0079 
0080         /* Check name string */
0081         TEST_ASSERT(strlen(pdesc->name) < header.name_size,
0082                 "KVM stats name (index: %d) too long", i);
0083 
0084         /* Check type,unit,base boundaries */
0085         TEST_ASSERT(type <= KVM_STATS_TYPE_MAX,
0086                 "Unknown KVM stats (%s) type: %u", pdesc->name, type);
0087         TEST_ASSERT(unit <= KVM_STATS_UNIT_MAX,
0088                 "Unknown KVM stats (%s) unit: %u", pdesc->name, unit);
0089         TEST_ASSERT(base <= KVM_STATS_BASE_MAX,
0090                 "Unknown KVM stats (%s) base: %u", pdesc->name, base);
0091 
0092         /*
0093          * Check exponent for stats unit
0094          * Exponent for counter should be greater than or equal to 0
0095          * Exponent for unit bytes should be greater than or equal to 0
0096          * Exponent for unit seconds should be less than or equal to 0
0097          * Exponent for unit clock cycles should be greater than or
0098          * equal to 0
0099          * Exponent for unit boolean should be 0
0100          */
0101         switch (pdesc->flags & KVM_STATS_UNIT_MASK) {
0102         case KVM_STATS_UNIT_NONE:
0103         case KVM_STATS_UNIT_BYTES:
0104         case KVM_STATS_UNIT_CYCLES:
0105             TEST_ASSERT(pdesc->exponent >= 0,
0106                     "Unsupported KVM stats (%s) exponent: %i",
0107                     pdesc->name, pdesc->exponent);
0108             break;
0109         case KVM_STATS_UNIT_SECONDS:
0110             TEST_ASSERT(pdesc->exponent <= 0,
0111                     "Unsupported KVM stats (%s) exponent: %i",
0112                     pdesc->name, pdesc->exponent);
0113             break;
0114         case KVM_STATS_UNIT_BOOLEAN:
0115             TEST_ASSERT(pdesc->exponent == 0,
0116                     "Unsupported KVM stats (%s) exponent: %d",
0117                     pdesc->name, pdesc->exponent);
0118             break;
0119         }
0120 
0121         /* Check size field, which should not be zero */
0122         TEST_ASSERT(pdesc->size,
0123                 "KVM descriptor(%s) with size of 0", pdesc->name);
0124         /* Check bucket_size field */
0125         switch (pdesc->flags & KVM_STATS_TYPE_MASK) {
0126         case KVM_STATS_TYPE_LINEAR_HIST:
0127             TEST_ASSERT(pdesc->bucket_size,
0128                     "Bucket size of Linear Histogram stats (%s) is zero",
0129                     pdesc->name);
0130             break;
0131         default:
0132             TEST_ASSERT(!pdesc->bucket_size,
0133                     "Bucket size of stats (%s) is not zero",
0134                     pdesc->name);
0135         }
0136         size_data += pdesc->size * sizeof(*stats_data);
0137     }
0138 
0139     /*
0140      * Now that the size of the data block is known, verify the data block
0141      * doesn't overlap the descriptor block.
0142      */
0143     TEST_ASSERT(header.data_offset >= header.desc_offset ||
0144             header.data_offset + size_data <= header.desc_offset,
0145             "Data block is overlapped with Descriptor block");
0146 
0147     /* Check validity of all stats data size */
0148     TEST_ASSERT(size_data >= header.num_desc * sizeof(*stats_data),
0149             "Data size is not correct");
0150 
0151     /* Check stats offset */
0152     for (i = 0; i < header.num_desc; ++i) {
0153         pdesc = get_stats_descriptor(stats_desc, i, &header);
0154         TEST_ASSERT(pdesc->offset < size_data,
0155                 "Invalid offset (%u) for stats: %s",
0156                 pdesc->offset, pdesc->name);
0157     }
0158 
0159     /* Allocate memory for stats data */
0160     stats_data = malloc(size_data);
0161     TEST_ASSERT(stats_data, "Allocate memory for stats data");
0162     /* Read kvm stats data as a bulk */
0163     ret = pread(stats_fd, stats_data, size_data, header.data_offset);
0164     TEST_ASSERT(ret == size_data, "Read KVM stats data");
0165     /* Read kvm stats data one by one */
0166     for (i = 0; i < header.num_desc; ++i) {
0167         pdesc = get_stats_descriptor(stats_desc, i, &header);
0168         read_stat_data(stats_fd, &header, pdesc, stats_data,
0169                    pdesc->size);
0170     }
0171 
0172     free(stats_data);
0173     free(stats_desc);
0174     free(id);
0175 }
0176 
0177 
0178 static void vm_stats_test(struct kvm_vm *vm)
0179 {
0180     int stats_fd = vm_get_stats_fd(vm);
0181 
0182     stats_test(stats_fd);
0183     close(stats_fd);
0184     TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed");
0185 }
0186 
0187 static void vcpu_stats_test(struct kvm_vcpu *vcpu)
0188 {
0189     int stats_fd = vcpu_get_stats_fd(vcpu);
0190 
0191     stats_test(stats_fd);
0192     close(stats_fd);
0193     TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed");
0194 }
0195 
0196 #define DEFAULT_NUM_VM      4
0197 #define DEFAULT_NUM_VCPU    4
0198 
0199 /*
0200  * Usage: kvm_bin_form_stats [#vm] [#vcpu]
0201  * The first parameter #vm set the number of VMs being created.
0202  * The second parameter #vcpu set the number of VCPUs being created.
0203  * By default, DEFAULT_NUM_VM VM and DEFAULT_NUM_VCPU VCPU for the VM would be
0204  * created for testing.
0205  */
0206 
0207 int main(int argc, char *argv[])
0208 {
0209     int i, j;
0210     struct kvm_vcpu **vcpus;
0211     struct kvm_vm **vms;
0212     int max_vm = DEFAULT_NUM_VM;
0213     int max_vcpu = DEFAULT_NUM_VCPU;
0214 
0215     /* Get the number of VMs and VCPUs that would be created for testing. */
0216     if (argc > 1) {
0217         max_vm = strtol(argv[1], NULL, 0);
0218         if (max_vm <= 0)
0219             max_vm = DEFAULT_NUM_VM;
0220     }
0221     if (argc > 2) {
0222         max_vcpu = strtol(argv[2], NULL, 0);
0223         if (max_vcpu <= 0)
0224             max_vcpu = DEFAULT_NUM_VCPU;
0225     }
0226 
0227     /* Check the extension for binary stats */
0228     TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD));
0229 
0230     /* Create VMs and VCPUs */
0231     vms = malloc(sizeof(vms[0]) * max_vm);
0232     TEST_ASSERT(vms, "Allocate memory for storing VM pointers");
0233 
0234     vcpus = malloc(sizeof(struct kvm_vcpu *) * max_vm * max_vcpu);
0235     TEST_ASSERT(vcpus, "Allocate memory for storing vCPU pointers");
0236 
0237     for (i = 0; i < max_vm; ++i) {
0238         vms[i] = vm_create_barebones();
0239         for (j = 0; j < max_vcpu; ++j)
0240             vcpus[i * max_vcpu + j] = __vm_vcpu_add(vms[i], j);
0241     }
0242 
0243     /* Check stats read for every VM and VCPU */
0244     for (i = 0; i < max_vm; ++i) {
0245         vm_stats_test(vms[i]);
0246         for (j = 0; j < max_vcpu; ++j)
0247             vcpu_stats_test(vcpus[i * max_vcpu + j]);
0248     }
0249 
0250     for (i = 0; i < max_vm; ++i)
0251         kvm_vm_free(vms[i]);
0252     free(vms);
0253     return 0;
0254 }