binary_stats.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * KVM binary statistics interface implementation
  4. *
  5. * Copyright 2021 Google LLC
  6. */
  7. #include <linux/kvm_host.h>
  8. #include <linux/kvm.h>
  9. #include <linux/errno.h>
  10. #include <linux/uaccess.h>
  11. /**
  12. * kvm_stats_read() - Common function to read from the binary statistics
  13. * file descriptor.
  14. *
  15. * @id: identification string of the stats
  16. * @header: stats header for a vm or a vcpu
  17. * @desc: start address of an array of stats descriptors for a vm or a vcpu
  18. * @stats: start address of stats data block for a vm or a vcpu
  19. * @size_stats: the size of stats data block pointed by @stats
  20. * @user_buffer: start address of userspace buffer
  21. * @size: requested read size from userspace
  22. * @offset: the start position from which the content will be read for the
  23. * corresponding vm or vcp file descriptor
  24. *
  25. * The file content of a vm/vcpu file descriptor is now defined as below:
  26. * +-------------+
  27. * | Header |
  28. * +-------------+
  29. * | id string |
  30. * +-------------+
  31. * | Descriptors |
  32. * +-------------+
  33. * | Stats Data |
  34. * +-------------+
  35. * Although this function allows userspace to read any amount of data (as long
  36. * as in the limit) from any position, the typical usage would follow below
  37. * steps:
  38. * 1. Read header from offset 0. Get the offset of descriptors and stats data
  39. * and some other necessary information. This is a one-time work for the
  40. * lifecycle of the corresponding vm/vcpu stats fd.
  41. * 2. Read id string from its offset. This is a one-time work for the lifecycle
  42. * of the corresponding vm/vcpu stats fd.
  43. * 3. Read descriptors from its offset and discover all the stats by parsing
  44. * descriptors. This is a one-time work for the lifecycle of the
  45. * corresponding vm/vcpu stats fd.
  46. * 4. Periodically read stats data from its offset using pread.
  47. *
  48. * Return: the number of bytes that has been successfully read
  49. */
  50. ssize_t kvm_stats_read(char *id, const struct kvm_stats_header *header,
  51. const struct _kvm_stats_desc *desc,
  52. void *stats, size_t size_stats,
  53. char __user *user_buffer, size_t size, loff_t *offset)
  54. {
  55. ssize_t len;
  56. ssize_t copylen;
  57. ssize_t remain = size;
  58. size_t size_desc;
  59. size_t size_header;
  60. void *src;
  61. loff_t pos = *offset;
  62. char __user *dest = user_buffer;
  63. size_header = sizeof(*header);
  64. size_desc = header->num_desc * sizeof(*desc);
  65. len = KVM_STATS_NAME_SIZE + size_header + size_desc + size_stats - pos;
  66. len = min(len, remain);
  67. if (len <= 0)
  68. return 0;
  69. remain = len;
  70. /*
  71. * Copy kvm stats header.
  72. * The header is the first block of content userspace usually read out.
  73. * The pos is 0 and the copylen and remain would be the size of header.
  74. * The copy of the header would be skipped if offset is larger than the
  75. * size of header. That usually happens when userspace reads stats
  76. * descriptors and stats data.
  77. */
  78. copylen = size_header - pos;
  79. copylen = min(copylen, remain);
  80. if (copylen > 0) {
  81. src = (void *)header + pos;
  82. if (copy_to_user(dest, src, copylen))
  83. return -EFAULT;
  84. remain -= copylen;
  85. pos += copylen;
  86. dest += copylen;
  87. }
  88. /*
  89. * Copy kvm stats header id string.
  90. * The id string is unique for every vm/vcpu, which is stored in kvm
  91. * and kvm_vcpu structure.
  92. * The id string is part of the stat header from the perspective of
  93. * userspace, it is usually read out together with previous constant
  94. * header part and could be skipped for later descriptors and stats
  95. * data readings.
  96. */
  97. copylen = header->id_offset + KVM_STATS_NAME_SIZE - pos;
  98. copylen = min(copylen, remain);
  99. if (copylen > 0) {
  100. src = id + pos - header->id_offset;
  101. if (copy_to_user(dest, src, copylen))
  102. return -EFAULT;
  103. remain -= copylen;
  104. pos += copylen;
  105. dest += copylen;
  106. }
  107. /*
  108. * Copy kvm stats descriptors.
  109. * The descriptors copy would be skipped in the typical case that
  110. * userspace periodically read stats data, since the pos would be
  111. * greater than the end address of descriptors
  112. * (header->header.desc_offset + size_desc) causing copylen <= 0.
  113. */
  114. copylen = header->desc_offset + size_desc - pos;
  115. copylen = min(copylen, remain);
  116. if (copylen > 0) {
  117. src = (void *)desc + pos - header->desc_offset;
  118. if (copy_to_user(dest, src, copylen))
  119. return -EFAULT;
  120. remain -= copylen;
  121. pos += copylen;
  122. dest += copylen;
  123. }
  124. /* Copy kvm stats values */
  125. copylen = header->data_offset + size_stats - pos;
  126. copylen = min(copylen, remain);
  127. if (copylen > 0) {
  128. src = stats + pos - header->data_offset;
  129. if (copy_to_user(dest, src, copylen))
  130. return -EFAULT;
  131. pos += copylen;
  132. }
  133. *offset = pos;
  134. return len;
  135. }