147 lines
4 KiB
C++
147 lines
4 KiB
C++
#include "kvmxx.hh"
|
|
#include "exception.hh"
|
|
#include "memmap.hh"
|
|
#include "identity.hh"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
|
|
namespace {
|
|
|
|
const int page_size = 4096;
|
|
int64_t nr_total_pages = 256 * 1024;
|
|
int64_t nr_slot_pages = 256 * 1024;
|
|
|
|
// Return the current time in nanoseconds.
|
|
uint64_t time_ns()
|
|
{
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec * (uint64_t)1000000000 + ts.tv_nsec;
|
|
}
|
|
|
|
// Update nr_to_write pages selected from nr_pages pages.
|
|
void write_mem(void* slot_head, int64_t nr_to_write, int64_t nr_pages)
|
|
{
|
|
char* var = static_cast<char*>(slot_head);
|
|
int64_t interval = nr_pages / nr_to_write;
|
|
|
|
for (int64_t i = 0; i < nr_to_write; ++i) {
|
|
++(*var);
|
|
var += interval * page_size;
|
|
}
|
|
}
|
|
|
|
// Let the guest update nr_to_write pages selected from nr_pages pages.
|
|
void do_guest_write(kvm::vcpu& vcpu, void* slot_head,
|
|
int64_t nr_to_write, int64_t nr_pages)
|
|
{
|
|
identity::vcpu guest_write_thread(vcpu, std::bind(write_mem, slot_head,
|
|
nr_to_write, nr_pages));
|
|
vcpu.run();
|
|
}
|
|
|
|
// Check how long it takes to update dirty log.
|
|
void check_dirty_log(kvm::vcpu& vcpu, mem_slot& slot, void* slot_head)
|
|
{
|
|
slot.set_dirty_logging(true);
|
|
slot.update_dirty_log();
|
|
|
|
for (int64_t i = 1; i <= nr_slot_pages; i *= 2) {
|
|
do_guest_write(vcpu, slot_head, i, nr_slot_pages);
|
|
|
|
uint64_t start_ns = time_ns();
|
|
int n = slot.update_dirty_log();
|
|
uint64_t end_ns = time_ns();
|
|
|
|
printf("get dirty log: %10lld ns for %10d dirty pages (expected %lld)\n",
|
|
end_ns - start_ns, n, i);
|
|
}
|
|
|
|
slot.set_dirty_logging(false);
|
|
}
|
|
|
|
}
|
|
|
|
void parse_options(int ac, char **av)
|
|
{
|
|
int opt;
|
|
char *endptr;
|
|
|
|
while ((opt = getopt(ac, av, "n:m:")) != -1) {
|
|
switch (opt) {
|
|
case 'n':
|
|
errno = 0;
|
|
nr_slot_pages = strtol(optarg, &endptr, 10);
|
|
if (errno || endptr == optarg) {
|
|
printf("dirty-log-perf: Invalid number: -n %s\n", optarg);
|
|
exit(1);
|
|
}
|
|
if (*endptr == 'k' || *endptr == 'K') {
|
|
nr_slot_pages *= 1024;
|
|
}
|
|
break;
|
|
case 'm':
|
|
errno = 0;
|
|
nr_total_pages = strtol(optarg, &endptr, 10);
|
|
if (errno || endptr == optarg) {
|
|
printf("dirty-log-perf: Invalid number: -m %s\n", optarg);
|
|
exit(1);
|
|
}
|
|
if (*endptr == 'k' || *endptr == 'K') {
|
|
nr_total_pages *= 1024;
|
|
}
|
|
break;
|
|
default:
|
|
printf("dirty-log-perf: Invalid option\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (nr_slot_pages > nr_total_pages) {
|
|
printf("dirty-log-perf: Invalid setting: slot %lld > mem %lld\n",
|
|
nr_slot_pages, nr_total_pages);
|
|
exit(1);
|
|
}
|
|
printf("dirty-log-perf: %lld slot pages / %lld mem pages\n",
|
|
nr_slot_pages, nr_total_pages);
|
|
}
|
|
|
|
int test_main(int ac, char **av)
|
|
{
|
|
kvm::system sys;
|
|
kvm::vm vm(sys);
|
|
mem_map memmap(vm);
|
|
|
|
parse_options(ac, av);
|
|
|
|
void* mem_head;
|
|
int64_t mem_size = nr_total_pages * page_size;
|
|
if (posix_memalign(&mem_head, page_size, mem_size)) {
|
|
printf("dirty-log-perf: Could not allocate guest memory.\n");
|
|
exit(1);
|
|
}
|
|
uint64_t mem_addr = reinterpret_cast<uintptr_t>(mem_head);
|
|
|
|
identity::hole hole(mem_head, mem_size);
|
|
identity::vm ident_vm(vm, memmap, hole);
|
|
kvm::vcpu vcpu(vm, 0);
|
|
|
|
uint64_t slot_size = nr_slot_pages * page_size;
|
|
uint64_t next_size = mem_size - slot_size;
|
|
uint64_t next_addr = mem_addr + slot_size;
|
|
mem_slot slot(memmap, mem_addr, slot_size, mem_head);
|
|
mem_slot other_slot(memmap, next_addr, next_size, (void *)next_addr);
|
|
|
|
// pre-allocate shadow pages
|
|
do_guest_write(vcpu, mem_head, nr_total_pages, nr_total_pages);
|
|
check_dirty_log(vcpu, slot, mem_head);
|
|
return 0;
|
|
}
|
|
|
|
int main(int ac, char** av)
|
|
{
|
|
return try_main(test_main, ac, av);
|
|
}
|