Commit 84292e56 authored by Peter Xu's avatar Peter Xu Committed by Paolo Bonzini

KVM: selftests: Add dirty ring buffer test

Add the initial dirty ring buffer test.

The current test implements the userspace dirty ring collection, by
only reaping the dirty ring when the ring is full.

So it's still running synchronously like this:

            vcpu                             main thread

  1. vcpu dirties pages
  2. vcpu gets dirty ring full
     (userspace exit)

                                       3. main thread waits until full
                                          (so hardware buffers flushed)
                                       4. main thread collects
                                       5. main thread continues vcpu

  6. vcpu continues, goes back to 1

We can't directly collects dirty bits during vcpu execution because
otherwise we can't guarantee the hardware dirty bits were flushed when
we collect and we're very strict on the dirty bits so otherwise we can
fail the future verify procedure.  A follow up patch will make this
test to support async just like the existing dirty log test, by adding
a vcpu kick mechanism.
Signed-off-by: default avatarPeter Xu <peterx@redhat.com>
Message-Id: <20201001012237.6111-1-peterx@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 60f644fb
...@@ -74,6 +74,7 @@ void kvm_vm_release(struct kvm_vm *vmp); ...@@ -74,6 +74,7 @@ void kvm_vm_release(struct kvm_vm *vmp);
void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log); void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log);
void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log,
uint64_t first_page, uint32_t num_pages); uint64_t first_page, uint32_t num_pages);
uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm);
int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, const vm_vaddr_t gva, int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, const vm_vaddr_t gva,
size_t len); size_t len);
...@@ -148,6 +149,7 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva); ...@@ -148,6 +149,7 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva);
struct kvm_run *vcpu_state(struct kvm_vm *vm, uint32_t vcpuid); struct kvm_run *vcpu_state(struct kvm_vm *vm, uint32_t vcpuid);
void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid);
int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid);
int vcpu_get_fd(struct kvm_vm *vm, uint32_t vcpuid);
void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid); void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid);
void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid, void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_guest_debug *debug); struct kvm_guest_debug *debug);
...@@ -201,6 +203,7 @@ void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid, ...@@ -201,6 +203,7 @@ void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid,
int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid, int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_nested_state *state, bool ignore_error); struct kvm_nested_state *state, bool ignore_error);
#endif #endif
void *vcpu_map_dirty_ring(struct kvm_vm *vm, uint32_t vcpuid);
const char *exit_reason_str(unsigned int exit_reason); const char *exit_reason_str(unsigned int exit_reason);
......
...@@ -114,6 +114,16 @@ int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id, ...@@ -114,6 +114,16 @@ int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id,
return r; return r;
} }
void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size)
{
struct kvm_enable_cap cap = { 0 };
cap.cap = KVM_CAP_DIRTY_LOG_RING;
cap.args[0] = ring_size;
vm_enable_cap(vm, &cap);
vm->dirty_ring_size = ring_size;
}
static void vm_open(struct kvm_vm *vm, int perm) static void vm_open(struct kvm_vm *vm, int perm)
{ {
vm->kvm_fd = open(KVM_DEV_PATH, perm); vm->kvm_fd = open(KVM_DEV_PATH, perm);
...@@ -328,6 +338,11 @@ void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, ...@@ -328,6 +338,11 @@ void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log,
__func__, strerror(-ret)); __func__, strerror(-ret));
} }
uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm)
{
return ioctl(vm->fd, KVM_RESET_DIRTY_RINGS);
}
/* /*
* Userspace Memory Region Find * Userspace Memory Region Find
* *
...@@ -432,10 +447,17 @@ struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid) ...@@ -432,10 +447,17 @@ struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid)
* *
* Removes a vCPU from a VM and frees its resources. * Removes a vCPU from a VM and frees its resources.
*/ */
static void vm_vcpu_rm(struct vcpu *vcpu) static void vm_vcpu_rm(struct kvm_vm *vm, struct vcpu *vcpu)
{ {
int ret; int ret;
if (vcpu->dirty_gfns) {
ret = munmap(vcpu->dirty_gfns, vm->dirty_ring_size);
TEST_ASSERT(ret == 0, "munmap of VCPU dirty ring failed, "
"rc: %i errno: %i", ret, errno);
vcpu->dirty_gfns = NULL;
}
ret = munmap(vcpu->state, sizeof(*vcpu->state)); ret = munmap(vcpu->state, sizeof(*vcpu->state));
TEST_ASSERT(ret == 0, "munmap of VCPU fd failed, rc: %i " TEST_ASSERT(ret == 0, "munmap of VCPU fd failed, rc: %i "
"errno: %i", ret, errno); "errno: %i", ret, errno);
...@@ -453,7 +475,7 @@ void kvm_vm_release(struct kvm_vm *vmp) ...@@ -453,7 +475,7 @@ void kvm_vm_release(struct kvm_vm *vmp)
int ret; int ret;
list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list) list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list)
vm_vcpu_rm(vcpu); vm_vcpu_rm(vmp, vcpu);
ret = close(vmp->fd); ret = close(vmp->fd);
TEST_ASSERT(ret == 0, "Close of vm fd failed,\n" TEST_ASSERT(ret == 0, "Close of vm fd failed,\n"
...@@ -1233,6 +1255,15 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid) ...@@ -1233,6 +1255,15 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid)
return rc; return rc;
} }
int vcpu_get_fd(struct kvm_vm *vm, uint32_t vcpuid)
{
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid);
return vcpu->fd;
}
void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid) void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid)
{ {
struct vcpu *vcpu = vcpu_find(vm, vcpuid); struct vcpu *vcpu = vcpu_find(vm, vcpuid);
...@@ -1561,6 +1592,42 @@ int _vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, ...@@ -1561,6 +1592,42 @@ int _vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid,
return ret; return ret;
} }
void *vcpu_map_dirty_ring(struct kvm_vm *vm, uint32_t vcpuid)
{
struct vcpu *vcpu;
uint32_t size = vm->dirty_ring_size;
TEST_ASSERT(size > 0, "Should enable dirty ring first");
vcpu = vcpu_find(vm, vcpuid);
TEST_ASSERT(vcpu, "Cannot find vcpu %u", vcpuid);
if (!vcpu->dirty_gfns) {
void *addr;
addr = mmap(NULL, size, PROT_READ,
MAP_PRIVATE, vcpu->fd,
vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET);
TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped private");
addr = mmap(NULL, size, PROT_READ | PROT_EXEC,
MAP_PRIVATE, vcpu->fd,
vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET);
TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped exec");
addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED, vcpu->fd,
vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET);
TEST_ASSERT(addr != MAP_FAILED, "Dirty ring map failed");
vcpu->dirty_gfns = addr;
vcpu->dirty_gfns_count = size / sizeof(struct kvm_dirty_gfn);
}
return vcpu->dirty_gfns;
}
/* /*
* VM Ioctl * VM Ioctl
* *
...@@ -1680,6 +1747,7 @@ static struct exit_reason { ...@@ -1680,6 +1747,7 @@ static struct exit_reason {
{KVM_EXIT_INTERNAL_ERROR, "INTERNAL_ERROR"}, {KVM_EXIT_INTERNAL_ERROR, "INTERNAL_ERROR"},
{KVM_EXIT_OSI, "OSI"}, {KVM_EXIT_OSI, "OSI"},
{KVM_EXIT_PAPR_HCALL, "PAPR_HCALL"}, {KVM_EXIT_PAPR_HCALL, "PAPR_HCALL"},
{KVM_EXIT_DIRTY_RING_FULL, "DIRTY_RING_FULL"},
#ifdef KVM_EXIT_MEMORY_NOT_PRESENT #ifdef KVM_EXIT_MEMORY_NOT_PRESENT
{KVM_EXIT_MEMORY_NOT_PRESENT, "MEMORY_NOT_PRESENT"}, {KVM_EXIT_MEMORY_NOT_PRESENT, "MEMORY_NOT_PRESENT"},
#endif #endif
......
...@@ -28,6 +28,9 @@ struct vcpu { ...@@ -28,6 +28,9 @@ struct vcpu {
uint32_t id; uint32_t id;
int fd; int fd;
struct kvm_run *state; struct kvm_run *state;
struct kvm_dirty_gfn *dirty_gfns;
uint32_t fetch_index;
uint32_t dirty_gfns_count;
}; };
struct kvm_vm { struct kvm_vm {
...@@ -52,6 +55,7 @@ struct kvm_vm { ...@@ -52,6 +55,7 @@ struct kvm_vm {
vm_vaddr_t tss; vm_vaddr_t tss;
vm_vaddr_t idt; vm_vaddr_t idt;
vm_vaddr_t handlers; vm_vaddr_t handlers;
uint32_t dirty_ring_size;
}; };
struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid); struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment