On Mon, May 17, 2021 at 02:53:12PM +0000, Jing Zhang wrote:
Provides a file descriptor per VM to read VM stats info/data. Provides a file descriptor per vCPU to read vCPU stats info/data.
Signed-off-by: Jing Zhang jingzhangos@google.com
arch/arm64/kvm/guest.c | 26 +++++ arch/mips/kvm/mips.c | 52 +++++++++ arch/powerpc/kvm/book3s.c | 52 +++++++++ arch/powerpc/kvm/booke.c | 45 ++++++++ arch/s390/kvm/kvm-s390.c | 117 ++++++++++++++++++++ arch/x86/kvm/x86.c | 53 +++++++++ include/linux/kvm_host.h | 127 ++++++++++++++++++++++ include/uapi/linux/kvm.h | 50 +++++++++ virt/kvm/kvm_main.c | 223 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 745 insertions(+)
+static ssize_t kvm_vcpu_stats_read(struct file *file, char __user *user_buffer,
size_t size, loff_t *offset)
+{
- char id[KVM_STATS_ID_MAXLEN];
- struct kvm_vcpu *vcpu = file->private_data;
- ssize_t copylen, len, remain = size;
- size_t size_header, size_desc, size_stats;
- loff_t pos = *offset;
- char __user *dest = user_buffer;
- void *src;
Nit. Better to do pointer arithmetic on a "char *". Note that gcc and clang will do the expected thing.
- snprintf(id, sizeof(id), "kvm-%d/vcpu-%d",
task_pid_nr(current), vcpu->vcpu_id);
- size_header = sizeof(kvm_vcpu_stats_header);
- size_desc =
kvm_vcpu_stats_header.count * sizeof(struct _kvm_stats_desc);
- size_stats = sizeof(vcpu->stat);
- len = sizeof(id) + size_header + size_desc + size_stats - pos;
- len = min(len, remain);
- if (len <= 0)
return 0;
- remain = len;
If 'desc_offset' is not right after the header, then the 'len' calculation is missing the gap into account. For example, assuming there is a gap of 0x1000000 between the header and the descriptors:
desc_offset = sizeof(id) + size_header + 0x1000000
and the user calls the ioctl with enough space for the whole file, including the gap:
*offset = 0 size = sizeof(id) + size_header + size_desc + size_stats + 0x1000000
then 'remain' gets the wrong size:
remain = sizeof(id) + size_header + size_desc + size_stats
and ... (more below)
- /* Copy kvm vcpu stats header id string */
- copylen = sizeof(id) - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)id + pos;
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vcpu stats header */
- copylen = sizeof(id) + size_header - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)&kvm_vcpu_stats_header;
src += pos - sizeof(id);
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vcpu stats descriptors */
- copylen = kvm_vcpu_stats_header.desc_offset + size_desc - pos;
This would be the state at this point:
pos = sizeof(id) + size_header copylen = sizeof(id) + size_header + 0x1000000 + size_desc - (sizeof(id) + size_header) = 0x1000000 + size_desc remain = size_desc + size_stats
- copylen = min(copylen, remain);
copylen = size_desc + size_stats
which is not enough to copy the descriptors (and the data).
- if (copylen > 0) {
src = (void *)&kvm_vcpu_stats_desc;
src += pos - kvm_vcpu_stats_header.desc_offset;
Moreover, src also needs to take the gap into account.
src = &kvm_vcpu_stats_desc + (sizeof(id) + size_header) - (sizeof(id) + size_header + 0x1000000) = &kvm_vcpu_stats_desc - 0x1000000
Otherwise, src ends up pointing at the wrong place.
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vcpu stats values */
- copylen = kvm_vcpu_stats_header.data_offset + size_stats - pos;
The same problem occurs here. There is a potential gap before data_offset that needs to be taken into account for src and len.
Would it be possible to just ensure that there is no gap? maybe even remove data_offset and desc_offset and always place them adjacent, and have the descriptors right after the header.
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)&vcpu->stat;
src += pos - kvm_vcpu_stats_header.data_offset;
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- *offset = pos;
- return len;
+}
+static ssize_t kvm_vm_stats_read(struct file *file, char __user *user_buffer,
size_t size, loff_t *offset)
+{
Consider moving the common code between kvm_vcpu_stats_read and this one into some function that takes pointers to header, desc, and data. Unless there is something vcpu or vm specific besides that.
- char id[KVM_STATS_ID_MAXLEN];
- struct kvm *kvm = file->private_data;
- ssize_t copylen, len, remain = size;
- size_t size_header, size_desc, size_stats;
- loff_t pos = *offset;
- char __user *dest = user_buffer;
- void *src;
- snprintf(id, sizeof(id), "kvm-%d", task_pid_nr(current));
- size_header = sizeof(kvm_vm_stats_header);
- size_desc = kvm_vm_stats_header.count * sizeof(struct _kvm_stats_desc);
- size_stats = sizeof(kvm->stat);
- len = sizeof(id) + size_header + size_desc + size_stats - pos;
- len = min(len, remain);
- if (len <= 0)
return 0;
- remain = len;
- /* Copy kvm vm stats header id string */
- copylen = sizeof(id) - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)id + pos;
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vm stats header */
- copylen = sizeof(id) + size_header - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)&kvm_vm_stats_header;
src += pos - sizeof(id);
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vm stats descriptors */
- copylen = kvm_vm_stats_header.desc_offset + size_desc - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)&kvm_vm_stats_desc;
src += pos - kvm_vm_stats_header.desc_offset;
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- /* Copy kvm vm stats values */
- copylen = kvm_vm_stats_header.data_offset + size_stats - pos;
- copylen = min(copylen, remain);
- if (copylen > 0) {
src = (void *)&kvm->stat;
src += pos - kvm_vm_stats_header.data_offset;
if (copy_to_user(dest, src, copylen))
return -EFAULT;
remain -= copylen;
pos += copylen;
dest += copylen;
- }
- *offset = pos;
- return len;
+}
-- 2.31.1.751.gd2f1c929bd-goog
kvmarm mailing list kvmarm@lists.cs.columbia.edu https://lists.cs.columbia.edu/mailman/listinfo/kvmarm