File size: 4,107 Bytes
8df6da4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#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);
}