Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * KVM binary statistics interface implementation
0004  *
0005  * Copyright 2021 Google LLC
0006  */
0007 
0008 #include <linux/kvm_host.h>
0009 #include <linux/kvm.h>
0010 #include <linux/errno.h>
0011 #include <linux/uaccess.h>
0012 
0013 /**
0014  * kvm_stats_read() - Common function to read from the binary statistics
0015  * file descriptor.
0016  *
0017  * @id: identification string of the stats
0018  * @header: stats header for a vm or a vcpu
0019  * @desc: start address of an array of stats descriptors for a vm or a vcpu
0020  * @stats: start address of stats data block for a vm or a vcpu
0021  * @size_stats: the size of stats data block pointed by @stats
0022  * @user_buffer: start address of userspace buffer
0023  * @size: requested read size from userspace
0024  * @offset: the start position from which the content will be read for the
0025  *          corresponding vm or vcp file descriptor
0026  *
0027  * The file content of a vm/vcpu file descriptor is now defined as below:
0028  * +-------------+
0029  * |   Header    |
0030  * +-------------+
0031  * |  id string  |
0032  * +-------------+
0033  * | Descriptors |
0034  * +-------------+
0035  * | Stats Data  |
0036  * +-------------+
0037  * Although this function allows userspace to read any amount of data (as long
0038  * as in the limit) from any position, the typical usage would follow below
0039  * steps:
0040  * 1. Read header from offset 0. Get the offset of descriptors and stats data
0041  *    and some other necessary information. This is a one-time work for the
0042  *    lifecycle of the corresponding vm/vcpu stats fd.
0043  * 2. Read id string from its offset. This is a one-time work for the lifecycle
0044  *    of the corresponding vm/vcpu stats fd.
0045  * 3. Read descriptors from its offset and discover all the stats by parsing
0046  *    descriptors. This is a one-time work for the lifecycle of the
0047  *    corresponding vm/vcpu stats fd.
0048  * 4. Periodically read stats data from its offset using pread.
0049  *
0050  * Return: the number of bytes that has been successfully read
0051  */
0052 ssize_t kvm_stats_read(char *id, const struct kvm_stats_header *header,
0053                const struct _kvm_stats_desc *desc,
0054                void *stats, size_t size_stats,
0055                char __user *user_buffer, size_t size, loff_t *offset)
0056 {
0057     ssize_t len;
0058     ssize_t copylen;
0059     ssize_t remain = size;
0060     size_t size_desc;
0061     size_t size_header;
0062     void *src;
0063     loff_t pos = *offset;
0064     char __user *dest = user_buffer;
0065 
0066     size_header = sizeof(*header);
0067     size_desc = header->num_desc * sizeof(*desc);
0068 
0069     len = KVM_STATS_NAME_SIZE + size_header + size_desc + size_stats - pos;
0070     len = min(len, remain);
0071     if (len <= 0)
0072         return 0;
0073     remain = len;
0074 
0075     /*
0076      * Copy kvm stats header.
0077      * The header is the first block of content userspace usually read out.
0078      * The pos is 0 and the copylen and remain would be the size of header.
0079      * The copy of the header would be skipped if offset is larger than the
0080      * size of header. That usually happens when userspace reads stats
0081      * descriptors and stats data.
0082      */
0083     copylen = size_header - pos;
0084     copylen = min(copylen, remain);
0085     if (copylen > 0) {
0086         src = (void *)header + pos;
0087         if (copy_to_user(dest, src, copylen))
0088             return -EFAULT;
0089         remain -= copylen;
0090         pos += copylen;
0091         dest += copylen;
0092     }
0093 
0094     /*
0095      * Copy kvm stats header id string.
0096      * The id string is unique for every vm/vcpu, which is stored in kvm
0097      * and kvm_vcpu structure.
0098      * The id string is part of the stat header from the perspective of
0099      * userspace, it is usually read out together with previous constant
0100      * header part and could be skipped for later descriptors and stats
0101      * data readings.
0102      */
0103     copylen = header->id_offset + KVM_STATS_NAME_SIZE - pos;
0104     copylen = min(copylen, remain);
0105     if (copylen > 0) {
0106         src = id + pos - header->id_offset;
0107         if (copy_to_user(dest, src, copylen))
0108             return -EFAULT;
0109         remain -= copylen;
0110         pos += copylen;
0111         dest += copylen;
0112     }
0113 
0114     /*
0115      * Copy kvm stats descriptors.
0116      * The descriptors copy would be skipped in the typical case that
0117      * userspace periodically read stats data, since the pos would be
0118      * greater than the end address of descriptors
0119      * (header->header.desc_offset + size_desc) causing copylen <= 0.
0120      */
0121     copylen = header->desc_offset + size_desc - pos;
0122     copylen = min(copylen, remain);
0123     if (copylen > 0) {
0124         src = (void *)desc + pos - header->desc_offset;
0125         if (copy_to_user(dest, src, copylen))
0126             return -EFAULT;
0127         remain -= copylen;
0128         pos += copylen;
0129         dest += copylen;
0130     }
0131 
0132     /* Copy kvm stats values */
0133     copylen = header->data_offset + size_stats - pos;
0134     copylen = min(copylen, remain);
0135     if (copylen > 0) {
0136         src = stats + pos - header->data_offset;
0137         if (copy_to_user(dest, src, copylen))
0138             return -EFAULT;
0139         pos += copylen;
0140     }
0141 
0142     *offset = pos;
0143     return len;
0144 }