-
Notifications
You must be signed in to change notification settings - Fork 107
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
implement KvmVec struct #6
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.
Noice! 👌
src/ioctls/kvm_vec.rs
Outdated
/// It is important to call `mem::forget` after using this vector. Otherwise rust will destroy it. | ||
/// | ||
unsafe fn as_vec(&mut self) -> Vec<F> { | ||
let entries_ptr = self.as_mut_original_struct().entries_mut().as_mut_ptr(); |
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.
nit: any reason why you're always using the accessor methods instead of just working with the struct members directly?
let entries_ptr = self.as_mut_original_struct().entries_mut().as_mut_ptr(); | |
let entries_ptr = self.kvm_array[0].entries_mut().as_mut_ptr(); |
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.
I just find it more readable this way. kvm_array[0]
looks weird. as_mut_original_struct
gives more information on what we're actually accessing.
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.
ok
src/ioctls/kvm_vec.rs
Outdated
|
||
let required_kvm_array_len = KvmVec::<T>::num_elements_to_kvm_array_len(self.len); | ||
unsafe { | ||
self.kvm_array.set_len(required_kvm_array_len); |
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.
nit: add comments for this function explaining the logic to make it easier to follow
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.
Done
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.
LGTM
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.
Please add usage examples for the public interface in doc comments. Some examples in this PR: #13
Can't think of meaningfull usage examples that would compile since KvmArray and KvmVec are private
|
@serban300 as discussed offline the trait needs to be public so you can access the methods associated with CpuId. If you do some more investigation and you discover that they actually don't need to be public, we can write the usage examples for either CpuId or for MSRs. |
@andreeaflorescu I added examples |
} | ||
} | ||
|
||
#[cfg(test)] | ||
/// Wrapper for `kvm_cpuid2`. |
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.
Please add details about CpuId. Right now when you open the documentation of CpuId it doesn't contain any relevant information about how to use the structure:
Type Definition kvm_ioctls::CpuId
type CpuId = KvmVec<kvm_cpuid2>;
[−]
Wrapper for kvm_cpuid2.
I would suggest two things:
- In the documentation description add a link to KvmVec. (e.g. CpuId implements trait 1 and trait 2 with methods for getting kvm_entries bla bla. And for the traits it implements you can add them as links to internal documentation. You can see some examples [here])(https://rust-lang-nursery.github.io/api-guidelines/documentation.html#prose-contains-hyperlinks-to-relevant-things-c-link).
- Add a minimal usage example for CpuId. You can copy one that already exist in the previous documentation. For example:
/// # Example
/// ```rust
/// use kvm_ioctls::{CpuId, Kvm, MAX_KVM_CPUID_ENTRIES};
/// let kvm = Kvm::new().unwrap();
/// let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap();
/// let cpuid_entries = cpuid.mut_entries_slice();
/// ```
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.
Same for MsrList.
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.
Done
src/ioctls/common/kvm_vec.rs
Outdated
fn entries_mut(&mut self) -> &mut __IncompleteArrayField<Self::Entry>; | ||
} | ||
|
||
/// An adapter that helps in treating a KvmArray more like an actual `Vec`. |
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.
"more like an actual" these are all filler words. Please rephrase.
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.
Done
src/ioctls/common/kvm_vec.rs
Outdated
|
||
/// Returns a reference to the actual KVM structure instance. | ||
/// | ||
pub fn as_original_struct(&self) -> &T { |
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.
nit: as_kvm_struct? Same for the others.
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.
Done. What do you mean by "the others" ?
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.
Probably I wanted to write this somewhere else, sorry for the confusion.
src/ioctls/common/kvm_vec.rs
Outdated
|
||
self.reserve(1); | ||
|
||
let mut entries = unsafe { self.as_vec() }; |
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.
Why is this safe? Please add comment before unsafe blocks.
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.
Done
src/ioctls/common/kvm_vec.rs
Outdated
impl<T: Default + KvmArray> KvmVec<T> { | ||
/// Returns the capacity required by kvm_array in order to hold the provided number of `KvmArray::Entry` | ||
/// | ||
fn num_elements_to_kvm_array_len(num_elements: usize) -> usize { |
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.
nit: I would rename this to kvm_vec_capacity. And also add a comment that it returns the kvm vec capacity required to hold num_elements
of KvmArray::Entry
.
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.
I think kvm_vec_capacity
would be confusing. I renamed kvm_array to 'mem_allocator' and I renamed this method to kvm_vec_len_to_mem_allocator_len
src/ioctls/common/kvm_vec.rs
Outdated
let additional_kvm_array_len = required_kvm_array_len - current_kvm_array_len; | ||
|
||
self.kvm_array.reserve(additional_kvm_array_len); | ||
self.capacity = desired_capacity; |
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.
The capacity should be updated to self.kvm_array.capacity().
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.
Done
@@ -42,3 +43,6 @@ pub use ioctls::{KvmRunWrapper, Result}; | |||
/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and | |||
/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). | |||
pub const MAX_KVM_CPUID_ENTRIES: usize = 80; | |||
|
|||
/// Maximum number of MSR entries that can be returned by a call to KVM ioctls. |
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.
Please add similar details as the MAX_KVM_CPUID_ENTRIES
have. Where did you get the value from? What can it be used for?
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.
I took it fro mthe prefious implementation of get_msr_index_list . I don't have solid details about this one.
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.
I can't find it either. Sadness :(
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.
As we discussed offline I believe from_entries
should not be deleted from CpuId.
I am not sure if this is useful for kvm_msr_list, but for kvm_cpuid2 we already know some scenarios where we can use it. Considering this I would suggest to add a CpuIdExtension (or similar) trait implemented for the type CpuId
src/ioctls/mod.rs
Outdated
#[cfg(test)] | ||
/// Wrapper for `kvm_cpuid2`. | ||
/// | ||
/// See the documentation for [KvmVec](struct.KvmVec.html) |
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.
Can you be a bit more explicit here? For example:
/// Wrapper over the `kvm_cpuid2` structure.
///
/// The `kvm_cpuid2` structure has a zero sized array. For details check the
/// [KVM API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
/// documentation on `kvm_cpuid2`. To provide safe access to
/// `kvm_cpuid_entry` elements, this type is implemented using
/// [KvmVec](struct.KvmVec.html).
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 ! Done.
src/ioctls/mod.rs
Outdated
} | ||
} | ||
|
||
/// Wrapper for `kvm_msr_list`. | ||
/// | ||
/// See the documentation for [KvmVec](struct.KvmVec.html) |
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.
Also elaborate on KvmMsrList here and add examples.
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.
Done.
@@ -42,3 +43,6 @@ pub use ioctls::{KvmRunWrapper, Result}; | |||
/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and | |||
/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). | |||
pub const MAX_KVM_CPUID_ENTRIES: usize = 80; | |||
|
|||
/// Maximum number of MSR entries that can be returned by a call to KVM ioctls. |
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.
I can't find it either. Sadness :(
Seems it's a common issue to deal with zero-sized array. Is there any best known methods for rust to support such a case? Or could we extend a basic rust-vmm crate to export helper macros/traits to deal with zero-sized array? |
If zero-sized arrays are needed outside of the kvm-ioctls crate then implementing them in a separate crate with helpers / commons sounds good to me. Anyway since I'm not very familiar with rust-vmm I would need to get more opinions on this issue. |
We can ask people in the rust-vmm list what they think about this. @serban300 can you please send an email? I believe the plan right now it to move the CpuId definition to vmm-vcpu. See rust-vmm/vmm-vcpu#3 |
KvmVec provides a common abstraction for all the KVM structures that ressemble arrays. Signed-off-by: Serban Iorga <seriorga@amazon.com>
Signed-off-by: Serban Iorga <seriorga@amazon.com>
Having a common crate for this sounds like a sane idea. You should look at the existing crates already present on crates.io, maybe you will find something there. What would be the name of the crate btw?
That's my understanding too! |
I am thinking that maybe we should instead place it in vmm-sys-utils instead of having a separate crate for it. I don't have a lot of experience here, but is this really a general problem? Do we think that in the future we will have to handle other 0-sized arrays besides the ones we have in KVM? I would be interested to see some other usecases for which this would need to live in a separate crate. |
I think it makes sense to move the traits of As things will no longer reside in a specific kvm-ioctls crate, and will instead be intended to be used in VMM-agnostic crates, could you rename things to avoid |
On a similar note, something I found while moving the old
|
It’s a common case for Linux ioctl related data structures, and Vhost has similar cases.
I think it’s ok to include it into the vmm-sys-utils crate.
… On Apr 24, 2019, at 8:37 AM, Andreea Florescu ***@***.*** ***@***.***>> wrote:
I am thinking that maybe we should instead place it in vmm-sys-utils instead of having a separate crate for it. I don't have a lot of experience here, but is this really a general problem? Do we think that in the future we will have to handle other 0-sized arrays besides the ones we have in KVM? I would be interested to see some other usecases for which this would need to live in a separate crate.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#6 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAOXR7GQZLBIDNT2MMSGR4DPR6TUVANCNFSM4HCKZ7EA>.
|
Thanks everyone for the feedback ! It would be nice if there was something similar on crates.io . I couldn't find anything so far. I will keep looking, but I think the chances are very slim. Anyway, it seems that there is a consensus around using the vmm-sys-util crate. It looks good to me also. I will submit a PR there as soon as rust-vmm/vmm-sys-util#1 is merged. Also I will make some changes in order to avoid KVM specific naming. |
/// | ||
fn len(&self) -> usize; | ||
|
||
/// Get the array length as mut |
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 comment looks wrong.
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.
Fixed
/// Get the capacity required by mem_allocator in order to hold | ||
/// the provided number of `KvmArray::Entry` | ||
/// | ||
fn kvm_vec_len_to_mem_allocator_len(kvm_vec_len: usize) -> usize { |
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.
I guess this could be renamed to mem_allocator_len
, since the name of the parameter kinda suggests what the conversion is about.
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.
Done
let required_mem_allocator_capacity = | ||
KvmVec::<T>::kvm_vec_len_to_mem_allocator_len(num_elements); | ||
|
||
let mut mem_allocator = Vec::with_capacity(required_mem_allocator_capacity); |
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.
I think lines 138-141 can be replaced with mem_allocator = vec![T::default(); required_mem_allocator_capacity]
, if it looks any better.
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, looks better. Done.
array_size_in_bytes / size_of::<T::Entry>() | ||
} | ||
|
||
/// Constructs a new KvmVec<T> that contains `num_elements` empty elements |
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.
It might be a bit more accurate to say "default-initialized" instead of "empty" here.
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.
Done
|
||
{ | ||
let kvm_vec_entries = kvm_vec.as_mut_kvm_struct().entries_mut(); | ||
// this is safe because the provided length is correct |
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 is for all the OCD ppl: it's nice if all the sentence-like (or longer comments) use proper capitalization and punctuation. I think there might be a couple of other places where that's not the case.
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.
Will try to fix all the comments.
|
||
self.reserve(1); | ||
|
||
let mut entries = self.as_vec(); |
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.
Is the self.as_vec
/push
combination required here? Since we've already ensured there is enough space via reserve
, it looks like we can call self.update_len(desired_len);
, and then update the value of the last position based on entry
using the mut slice returned by self.as_mut_entries_slice
.
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.
Nice. Seems to work.
/// * `f` - The function used to evaluate whether an entry will be kept or not. | ||
/// When `f` returns `true` the entry is kept. | ||
/// | ||
pub fn retain<P>(&mut self, f: P) |
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.
If retain
remains the sole user of as_vec
, which is pretty weird/scary as currently implemented, I would consider reimplementing this without as_vec
(for example, using slice::sort_by
, and dropping the tail; or writing the iterations + swap ourselves if that's deemed too inefficient), and removing as_vec
altogether, to get rid of the extra unsafe
-ness and prevent future use.
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.
Good point. Done. I implemented some custom logic for this since it's quite easy.
|
||
impl<T: Default + KvmArray> PartialEq for KvmVec<T> { | ||
fn eq(&self, other: &KvmVec<T>) -> bool { | ||
self.len == other.len && self.as_entries_slice() == other.as_entries_slice() |
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.
Is self.len == other.len
implied by self.as_entries_slice() == other.as_entries_slice()
?
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.
Not necessarily. If something goes wrong self.len can differ from T.len()
|
||
let num_bytes = self.mem_allocator.len() * size_of::<T>(); | ||
let src_byte_slice = | ||
unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, num_bytes) }; |
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.
Courtesy comment regarding why the unsafe block is safe is nice.
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.
I changed the implementation in order to avoid using unsafe:
clone.mem_allocator = self.mem_allocator.to_vec();
let src_byte_slice = | ||
unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, num_bytes) }; | ||
let dst_byte_slice = | ||
unsafe { std::slice::from_raw_parts_mut(clone.as_mut_ptr() as *mut u8, num_bytes) }; |
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.
Ditto.
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.
The same comment as above.
Closing in favor of rust-vmm/vmm-sys-util#4 |
KvmVec provides a common abstraction for all the KVM structures that resemble arrays. For example: CpuId and MsrList