-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable usage of sync_regs #190
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ctfhacker thanks for your contribution. It looks like the tests are failing on arm, if this is only available on x86_64, could you mark the code as such so that the tests pass on arm as well? Can you also improve your commit message by:
- Writing in the commit message what is the sync_regs functionality useful for
- Sign your commit by running
git commit -s --amend --no-edit
2ca05d6
to
db82986
Compare
Perfect! Let me know how this looks now with the checks passing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a really nice PR to review, thanks for your contribution. I really appreciate that you took the time to write documentation, examples, and tests! Also, really nice how you structured the reg
parameter such that invalid values cannot be passed 👍
I have just a few small comments, otherwise LGTM.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] | ||
pub fn sync_regs(&self) -> kvm_sync_regs { | ||
let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); | ||
unsafe { kvm_run.s.regs } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe code needs to have a comment about why is it safe. You can take a look at some examples here:
- https://github.com/rust-vmm/kvm-ioctls/blob/97ff779b6ea96ae451308149b0ae38f13b271c19/src/ioctls/vcpu.rs#L1347
- https://github.com/rust-vmm/kvm-ioctls/blob/97ff779b6ea96ae451308149b0ae38f13b271c19/src/ioctls/vcpu.rs#L1223
The unsafeness in this case comes from accessing the union field, so we need to write about why is it safe to access.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the perspective of an arbitrary vcpu, I'm not sure we have the safety guarantee that the kvm_run
pointer was mmap
'ed with enough space for the sync_regs
itself.
I was looking at adding a sanity check that the KvmRunWrapper.mmap_size
is large enough to fill the kvm_run
structure, but wasn't quite sure if that was the best way.
Or, could we make the both sync_regs
and sync_regs_mut
themselves unsafe
, since we can't guarantee that the pointer from kvm_run_ptr
is large enough for the entire kvm_run
structure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking them as unsafe is a good path forward if we cannot ensure that using it is safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the mmap_size
check didn't work? It looks like the next best thing if 'has space for sync_regs
' can't be built in to the type. Returning an Result
from sync_regs_mut
might be awkward but maybe better than the unsafe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! KvmRunWrapper
does keep the mmap_size
around in the structure which we can check against. For some reason, I thought KvmRunWrapper
could be created from an arbitrary pointer.
I'll see about adding that check in.
My thinking was because anyone can create a KvmRunWrapper
, then calling sync_regs
which relies on that kvm_run_ptr
can't be trusted (or at least isn't safe in all cases). I think adding a check against the kvm_run_ptr.mmap_size
should suffice. I don't see a problem adding a Result
for that either. Only question would be looking into if the kvm_run
parameters have changed in versions or how to get the correct offset/length for any given bindings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having mmap_size be correct is guaranteed by KvmRunWrapper
. Getting that bit of unsafe code right is already critical to the safety of the system and allows other parts to rely on the type being correct if it exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that explanation. I saw that KvmRunWrapper
is exposed by the library, enabling the user to specify whatever mmap_size
they wished. With that though, the user can't use this smaller KvmRunWrapper
to construct an invalid Vcpu
, so everything looks fine.
Thank you for spelling that out for me. Much appreciated! I'll remove those unsafe
s.
src/ioctls/vcpu.rs
Outdated
vcpu.set_sync_valid_reg(SyncReg::Register); | ||
assert_eq!( | ||
vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, | ||
SyncReg::Register as u64 | ||
); | ||
vcpu.clear_sync_valid_reg(SyncReg::Register); | ||
assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); | ||
|
||
vcpu.set_sync_valid_reg(SyncReg::SystemRegister); | ||
assert_eq!( | ||
vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, | ||
SyncReg::SystemRegister as u64 | ||
); | ||
vcpu.clear_sync_valid_reg(SyncReg::SystemRegister); | ||
assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); | ||
|
||
vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); | ||
assert_eq!( | ||
vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, | ||
SyncReg::VcpuEvents as u64 | ||
); | ||
vcpu.clear_sync_valid_reg(SyncReg::VcpuEvents); | ||
assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code block and the ones below with sync_dirty_reg
can each be reduced to the following (without loosing too much of the code readability):
let sync_regs = [SyncReg::Register, SyncReg::SystemRegister, SyncReg::VcpuEvents];
for reg in sync_regs {
vcpu.set_sync_valid_reg(reg);
assert_eq!(
vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs,
SyncReg::Register as u64
);
vcpu.clear_sync_valid_reg(reg);
assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0);
}
Would you be okay with this change to remove some of the code duplication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure. That makes total sense to me.
8092c2f
to
7d6f26d
Compare
You'll need to do a borrow on the array used in tests to fix the problems reported by the CI. Once that is done, I think we can merge it. |
The coverage score seemed to drop from 86.1 -> 85.4. Should I update that? I think all the new code has unit tests for them. |
Yes, you should just update the coverage score. The coverage is a bit too sensitive. We will work on addressing that at the project level. |
33129ba
to
4993392
Compare
Roger that! Just updated and pushed that coverage change. Apologies if |
fe5a833
to
9137a2a
Compare
44585ac
to
03276fa
Compare
src/ioctls/vcpu.rs
Outdated
// hlt is the only expected return from guest execution | ||
assert!(matches!(vcpu.run().expect("run failed"), VcpuExit::Hlt)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When running this test on my laptop, this is failing with:
thread 'ioctls::vcpu::tests::test_sync_regs_with_run' panicked at 'assertion failed: matches!(vcpu.run().expect(\"run failed\"), VcpuExit :: Hlt)', src/ioctls/vcpu.rs:2648:9
Is there something else (a capability, etc) we need to check before doing this assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, there is the KVM_CAP_SYNC_REGS
capability, containing which sync_regs are available. We can add the assert in the unit tests with something like
let sync_regs_cap = unsafe {
unsafe { ioctl_with_val(&vm, KVM_CHECK_EXTENSION(), Cap::SyncRegs as c_ulong) }
};
assert_eq!(sync_regs_cap, 7);
or opening up check_extension_int
to pub
rather than private.
Either way, I think we'll also need to add this to the example code to make sure the user knows to check the capability.
Any thoughts on opening check_extension_int
to pub
? I've never seen it, but supposedly that capability could come back with fewer than 3 bits set, signifying that one of the three sync_regs
isn't in use. Opening up check_extension_int
would give the user the ability to look specifically which bits are not set rather than an all or nothing, but I'm not sure opening that up function to this one use case is worthwhile.
I've pushed this test case up to see if your capability isn't 7 to see if that was the problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that was the problem. We have the check_extension
method that is public and can be used for that, i.e.:
if kvm.check_extension(Cap::SyncRegs) { ... }
And yes we should also update the documentation examples. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides the comment about the failing test, looks good to me!
The coverage file might need to be updated again :(.
bd87348
to
dbc1f95
Compare
src/ioctls/vcpu.rs
Outdated
vcpu.set_sync_dirty_reg(SyncReg::VcpuEvents); | ||
|
||
// Ensure sync reg bits are available for use | ||
assert!(kvm.check_extension(Cap::SyncRegs)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way the test is still going to fail on some machines. I propose we run this test only if we have the capability, similar to how it is done here:
if kvm.check_extension(Cap::SyncRegs) {
let vm = kvm.create_vm().unwrap();
// This example is based on https://lwn.net/Articles/658511/
...
assert_eq!(sync_regs.regs.rax, 0x8001);
}
I just noticed there are a few places where we are going with the assert
approach, but we should probably update them.
src/ioctls/vcpu.rs
Outdated
let vm = kvm.create_vm().unwrap(); | ||
let mut vcpu = vm.create_vcpu(0).unwrap(); | ||
|
||
if kvm.check_extension(Cap::SyncRegs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting the sync registers works on every machine, so I think we can run the current test without checking for the extension. My proposal was to check for it only for the other test where we call run
as well. Same thing applies for the documentation tests. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah that makes sense. I agree with that. It isn't technically going to be a problem until run
is called, rather than hinting ahead of time.
KVM provides the ability to synchronize general purpose registers, system registers, and vcpu events without the need of an ioctl. This is worthwhile for VMs that exit frequently to avoid calling `get_regs`, `get_sregs`, or `get_vcpu_events` each exit or `set_regs`, `set_sregs`, or `set_vcpu_events` on each entry. Signed-off-by: Cory Duplantis <cld251@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Enable the use of the
sync_regs
API from KVM to allow bulk getting and setting of general purpose registers, system registers, and vcpu events, reducing the number of ioctls needed.