This patch adds 'kvm stat' command that allows retrieving statistics out of a running guest. Currently the only supported statistics are memory statistics, available using the '--memory' parameter. Signed-off-by: Sasha Levin <levinsasha928@xxxxxxxxx> --- tools/kvm/Documentation/kvm-stat.txt | 19 ++++++ tools/kvm/Makefile | 1 + tools/kvm/builtin-stat.c | 79 +++++++++++++++++++++++++ tools/kvm/command-list.txt | 1 + tools/kvm/include/kvm/builtin-stat.h | 7 ++ tools/kvm/include/kvm/kvm.h | 1 + tools/kvm/kvm-cmd.c | 2 + tools/kvm/virtio/balloon.c | 108 ++++++++++++++++++++++++++++++++- 8 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 tools/kvm/Documentation/kvm-stat.txt create mode 100644 tools/kvm/builtin-stat.c create mode 100644 tools/kvm/include/kvm/builtin-stat.h diff --git a/tools/kvm/Documentation/kvm-stat.txt b/tools/kvm/Documentation/kvm-stat.txt new file mode 100644 index 0000000..ce5ab54 --- /dev/null +++ b/tools/kvm/Documentation/kvm-stat.txt @@ -0,0 +1,19 @@ +kvm-stat(1) +================ + +NAME +---- +kvm-stat - Print statistics about a running instance + +SYNOPSIS +-------- +[verse] +'kvm [command] [-n instance] [-p instance pid] [--all]' + +DESCRIPTION +----------- +The command prints statistics about a running instance. +For a list of running instances see 'kvm list'. + +Commands: + --memory, -m Display memory statistics diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 3a06e10..85bbce7 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -24,6 +24,7 @@ OBJS += builtin-balloon.o OBJS += builtin-debug.o OBJS += builtin-help.o OBJS += builtin-list.o +OBJS += builtin-stat.o OBJS += builtin-pause.o OBJS += builtin-resume.o OBJS += builtin-run.o diff --git a/tools/kvm/builtin-stat.c b/tools/kvm/builtin-stat.c new file mode 100644 index 0000000..d9bc75d --- /dev/null +++ b/tools/kvm/builtin-stat.c @@ -0,0 +1,79 @@ +#include <kvm/util.h> +#include <kvm/kvm-cmd.h> +#include <kvm/builtin-stat.h> +#include <kvm/kvm.h> +#include <kvm/parse-options.h> + +#include <stdio.h> +#include <string.h> +#include <signal.h> + +static bool mem; +static bool all; +static u64 instance_pid; +static const char *instance_name; + +static const char * const stat_usage[] = { + "kvm stat [command] [--all] [-n name] [-p pid]", + NULL +}; + +static const struct option stat_options[] = { + OPT_GROUP("Commands options:"), + OPT_BOOLEAN('m', "memory", &mem, "Display memory statistics"), + OPT_GROUP("Instance options:"), + OPT_BOOLEAN('a', "all", &all, "All instances"), + OPT_STRING('n', "name", &instance_name, "name", "Instance name"), + OPT_U64('p', "pid", &instance_pid, "Instance pid"), + OPT_END() +}; + +static void parse_stat_options(int argc, const char **argv) +{ + while (argc != 0) { + argc = parse_options(argc, argv, stat_options, stat_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (argc != 0) + kvm_stat_help(); + } +} + +void kvm_stat_help(void) +{ + usage_with_options(stat_usage, stat_options); +} + +static int do_memstat(const char *name, int pid) +{ + printf("Sending memstat command to %s, output should be on the targets' terminal.\n", name); + return kill(pid, SIGKVMMEMSTAT); +} + +int kvm_cmd_stat(int argc, const char **argv, const char *prefix) +{ + parse_stat_options(argc, argv); + + if (!mem) + usage_with_options(stat_usage, stat_options); + + if (mem && all) + return kvm__enumerate_instances(do_memstat); + + if (instance_name == NULL && + instance_pid == 0) + kvm_stat_help(); + + if (instance_name) + instance_pid = kvm__get_pid_by_instance(instance_name); + + if (instance_pid <= 0) + die("Failed locating instance"); + + if (mem) { + printf("Sending memstat command to designated instance, output should be on the targets' terminal.\n"); + + return kill(instance_pid, SIGKVMMEMSTAT); + } + + return 0; +} diff --git a/tools/kvm/command-list.txt b/tools/kvm/command-list.txt index 6a49d0a..f201d75 100644 --- a/tools/kvm/command-list.txt +++ b/tools/kvm/command-list.txt @@ -10,3 +10,4 @@ kvm-list common kvm-debug common kvm-balloon common kvm-stop common +kvm-stat common diff --git a/tools/kvm/include/kvm/builtin-stat.h b/tools/kvm/include/kvm/builtin-stat.h new file mode 100644 index 0000000..e3ce292 --- /dev/null +++ b/tools/kvm/include/kvm/builtin-stat.h @@ -0,0 +1,7 @@ +#ifndef KVM__STAT_H +#define KVM__STAT_H + +int kvm_cmd_stat(int argc, const char **argv, const char *prefix); +void kvm_stat_help(void); + +#endif diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h index d4fe2a1..c2d815c 100644 --- a/tools/kvm/include/kvm/kvm.h +++ b/tools/kvm/include/kvm/kvm.h @@ -22,6 +22,7 @@ #define SIGKVMDELMEM (SIGRTMIN + 3) #define SIGKVMSTOP (SIGRTMIN + 4) #define SIGKVMRESUME (SIGRTMIN + 5) +#define SIGKVMMEMSTAT (SIGRTMIN + 6) struct kvm { int sys_fd; /* For system ioctls(), i.e. /dev/kvm */ diff --git a/tools/kvm/kvm-cmd.c b/tools/kvm/kvm-cmd.c index 4e3ea22..8fc6d22 100644 --- a/tools/kvm/kvm-cmd.c +++ b/tools/kvm/kvm-cmd.c @@ -12,6 +12,7 @@ #include "kvm/builtin-list.h" #include "kvm/builtin-version.h" #include "kvm/builtin-stop.h" +#include "kvm/builtin-stat.h" #include "kvm/builtin-help.h" #include "kvm/kvm-cmd.h" #include "kvm/builtin-run.h" @@ -26,6 +27,7 @@ struct cmd_struct kvm_commands[] = { { "version", kvm_cmd_version, NULL, 0 }, { "--version", kvm_cmd_version, NULL, 0 }, { "stop", kvm_cmd_stop, kvm_stop_help, 0 }, + { "stat", kvm_cmd_stat, kvm_stat_help, 0 }, { "help", kvm_cmd_help, NULL, 0 }, { "run", kvm_cmd_run, kvm_run_help, 0 }, { NULL, NULL, NULL, 0 }, diff --git a/tools/kvm/virtio/balloon.c b/tools/kvm/virtio/balloon.c index 983a114..c5853a5 100644 --- a/tools/kvm/virtio/balloon.c +++ b/tools/kvm/virtio/balloon.c @@ -21,10 +21,11 @@ #include <sys/stat.h> #include <pthread.h> -#define NUM_VIRT_QUEUES 2 +#define NUM_VIRT_QUEUES 3 #define VIRTIO_BLN_QUEUE_SIZE 128 #define VIRTIO_BLN_INFLATE 0 #define VIRTIO_BLN_DEFLATE 1 +#define VIRTIO_BLN_STATS 2 struct bln_dev { struct pci_device_header pci_hdr; @@ -41,6 +42,12 @@ struct bln_dev { struct virt_queue vqs[NUM_VIRT_QUEUES]; struct thread_pool__job jobs[NUM_VIRT_QUEUES]; + struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; + struct virtio_balloon_stat *cur_stat; + u32 cur_stat_head; + u16 stat_count; + int stat_waitfd; + struct virtio_balloon_config config; }; @@ -127,7 +134,7 @@ static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, stru if (queue == &bdev->vqs[VIRTIO_BLN_INFLATE]) { madvise(guest_ptr, 1 << VIRTIO_BALLOON_PFN_SHIFT, MADV_DONTNEED); bdev->config.actual++; - } else { + } else if (queue == &bdev->vqs[VIRTIO_BLN_DEFLATE]) { bdev->config.actual--; } } @@ -137,10 +144,46 @@ static bool virtio_bln_do_io_request(struct kvm *kvm, struct bln_dev *bdev, stru return true; } +static bool virtio_bln_do_stat_request(struct kvm *kvm, struct bln_dev *bdev, struct virt_queue *queue) +{ + struct iovec iov[VIRTIO_BLN_QUEUE_SIZE]; + u16 out, in, head; + struct virtio_balloon_stat *stat; + u64 wait_val = 1; + + head = virt_queue__get_iov(queue, iov, &out, &in, kvm); + stat = iov[0].iov_base; + + /* Initial empty stat buffer */ + if (bdev->cur_stat == NULL) { + bdev->cur_stat = stat; + bdev->cur_stat_head = head; + + return true; + } + + memcpy(bdev->stats, stat, iov[0].iov_len); + + bdev->stat_count = iov[0].iov_len / sizeof(struct virtio_balloon_stat); + bdev->cur_stat = stat; + bdev->cur_stat_head = head; + + if (write(bdev->stat_waitfd, &wait_val, sizeof(wait_val)) <= 0) + return -EFAULT; + + return 1; +} + static void virtio_bln_do_io(struct kvm *kvm, void *param) { struct virt_queue *vq = param; + if (vq == &bdev.vqs[VIRTIO_BLN_STATS]) { + virtio_bln_do_stat_request(kvm, &bdev, vq); + virt_queue__trigger_irq(vq, bdev.pci_hdr.irq_line, &bdev.isr, kvm); + return; + } + while (virt_queue__available(vq)) { virtio_bln_do_io_request(kvm, &bdev, vq); virt_queue__trigger_irq(vq, bdev.pci_hdr.irq_line, &bdev.isr, kvm); @@ -218,15 +261,70 @@ static struct ioport_operations virtio_bln_io_ops = { .io_out = virtio_bln_pci_io_out, }; +static int virtio_bln__collect_stats(void) +{ + u64 tmp; + + virt_queue__set_used_elem(&bdev.vqs[VIRTIO_BLN_STATS], bdev.cur_stat_head, + sizeof(struct virtio_balloon_stat)); + virt_queue__trigger_irq(&bdev.vqs[VIRTIO_BLN_STATS], bdev.pci_hdr.irq_line, + &bdev.isr, kvm); + + if (read(bdev.stat_waitfd, &tmp, sizeof(tmp)) <= 0) + return -EFAULT; + + return 0; +} + +static int virtio_bln__print_stats(void) +{ + u16 i; + + if (virtio_bln__collect_stats() < 0) + return -EFAULT; + + printf("\n\n\t*** Guest memory statistics ***\n\n"); + for (i = 0; i < bdev.stat_count; i++) { + switch (bdev.stats[i].tag) { + case VIRTIO_BALLOON_S_SWAP_IN: + printf("The amount of memory that has been swapped in (in bytes):"); + break; + case VIRTIO_BALLOON_S_SWAP_OUT: + printf("The amount of memory that has been swapped out to disk (in bytes):"); + break; + case VIRTIO_BALLOON_S_MAJFLT: + printf("The number of major page faults that have occurred:"); + break; + case VIRTIO_BALLOON_S_MINFLT: + printf("The number of minor page faults that have occurred:"); + break; + case VIRTIO_BALLOON_S_MEMFREE: + printf("The amount of memory not being used for any purpose (in bytes):"); + break; + case VIRTIO_BALLOON_S_MEMTOT: + printf("The total amount of memory available (in bytes):"); + break; + } + printf("%llu\n", bdev.stats[i].val); + } + printf("\n"); + + return 0; +} + static void handle_sigmem(int sig) { if (sig == SIGKVMADDMEM) { bdev.config.num_pages += 256; - } else { + } else if (sig == SIGKVMDELMEM) { if (bdev.config.num_pages < 256) return; bdev.config.num_pages -= 256; + } else if (sig == SIGKVMMEMSTAT) { + virtio_bln__print_stats(); + + return; } /* Notify that the configuration space has changed */ @@ -241,6 +339,7 @@ void virtio_bln__init(struct kvm *kvm) signal(SIGKVMADDMEM, handle_sigmem); signal(SIGKVMDELMEM, handle_sigmem); + signal(SIGKVMMEMSTAT, handle_sigmem); bdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_bln_io_ops, IOPORT_SIZE, &bdev); @@ -262,7 +361,8 @@ void virtio_bln__init(struct kvm *kvm) bdev.pci_hdr.irq_pin = pin; bdev.pci_hdr.irq_line = line; - bdev.host_features = 0; + bdev.host_features = 1 << VIRTIO_BALLOON_F_STATS_VQ; + bdev.stat_waitfd = eventfd(0, 0); memset(&bdev.config, 0, sizeof(struct virtio_balloon_config)); pci__register(&bdev.pci_hdr, dev); -- 1.7.6 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html