Skip to content
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

Dummy PR for proxy bindings #3

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft

Dummy PR for proxy bindings #3

wants to merge 3 commits into from

Conversation

alexandruag
Copy link
Owner

No description provided.

@jiangliu
Copy link

jiangliu commented Sep 8, 2020

Once we have run into a trap to separate data structs and their state structs.
The trap may get solved if we will group kvm-ioctls, kvm-bindings, kvm-states into a workspace.

Copy link

@acatangiu acatangiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

use super::*;

#[test]
fn bindgen_test_layout_kvm_pic_state() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these structs are shadowing the upstream ones, I think the layout tests for them should compare size, alignment, etc against the upstream struct instead of hardcoded values.
This would improve on the mechanisms to keep them in sync.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what wasn't clearly presented is the layout tests are actually the ones generated for the upstream bindings, and so we know the structs match.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exact layout tests are generated by upstream, but they're now disconnected from upstream.

If we upgrade used upstream version, the values hardcoded here (generated with previous upstream version) might be stale.

If these tests would instead compare local structs against imported/upstream structs, the code here would not get stale and would work correctly for any upstream version.

assert_eq!(align_of::<T>(), align_of::<U>());
assert_eq!(size_of::<T>(), size_of::<U>());
// Safe because `src` is aligned, the two types have the same size and alignment,
// and they are both plain old data structs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to guarantee this? Maybe force them through a dummy FFI or something similar to make sure we don't accidentally break this assumption?

My worry is that the function safety is based on only using it for POD or repr(C) data, but that isn't in any way enforced, and we could accidentally misuse it.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption must hold for kvm bindings, and the unit tests and sanity checks above verify everything except the presence of #[repr(C)], which is where the dummy FFI might come in handy for extra certainty.

// macro (i.e. `use kvm_bindings as upstream;`). It also requires the `convert_bitwise`
// function above to be in scope.
#[macro_export]
macro_rules! impl_conversions {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool how conversion is ultimately only casting and thus no/low overhead 👍

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this is not just casting, but rather equivalent to a memcpy. My initial measurements didn't detect significant overhead, so maybe it's the compiler that in the end applies some more simplifications.


/// Helper function for FamStructWrapper conversion. Can/should be replaced by implementing
/// `From` for `FamStructWrapper` in `vmm-sys-util`.
pub fn convert_fam_struct_wrapper<'a, T, U>(src: &'a FamStructWrapper<T>) -> FamStructWrapper<U>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is reallocation with deep copy, is there any way to do some trickery here as well?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extent of the copying is comparable to the conversion above, but it can be made to look simpler :D

@acatangiu
Copy link

How does the versioning-by-proxy work when adding a new field to one of the versioned structs?

I think an example for that would be very useful in getting a complete picture of how the proxy approach works.

Copy link
Owner Author

@alexandruag alexandruag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking and the comments everyone! Gerry, can you share more details about the trap you mention? Sounds like an interesting situation. Regarding the example, I don't have anything at hand but it should be exactly the same as for the other approach; what's different here is where the Versionize-aware definitions are kept.

use super::*;

#[test]
fn bindgen_test_layout_kvm_pic_state() {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what wasn't clearly presented is the layout tests are actually the ones generated for the upstream bindings, and so we know the structs match.

assert_eq!(align_of::<T>(), align_of::<U>());
assert_eq!(size_of::<T>(), size_of::<U>());
// Safe because `src` is aligned, the two types have the same size and alignment,
// and they are both plain old data structs.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption must hold for kvm bindings, and the unit tests and sanity checks above verify everything except the presence of #[repr(C)], which is where the dummy FFI might come in handy for extra certainty.

// macro (i.e. `use kvm_bindings as upstream;`). It also requires the `convert_bitwise`
// function above to be in scope.
#[macro_export]
macro_rules! impl_conversions {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this is not just casting, but rather equivalent to a memcpy. My initial measurements didn't detect significant overhead, so maybe it's the compiler that in the end applies some more simplifications.


/// Helper function for FamStructWrapper conversion. Can/should be replaced by implementing
/// `From` for `FamStructWrapper` in `vmm-sys-util`.
pub fn convert_fam_struct_wrapper<'a, T, U>(src: &'a FamStructWrapper<T>) -> FamStructWrapper<U>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extent of the copying is comparable to the conversion above, but it can be made to look simpler :D

Copy link

@acatangiu acatangiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the example, I don't have anything at hand but it should be exactly the same as for the other approach; what's different here is where the Versionize-aware definitions are kept.

In theory it should work the same, but I'm having a hard time getting my head wrapped around those conversions when we have multiple versions/layouts of a struct.

The mechanisms here are quite complex and adding a practical example would greatly help understanding things better.

use super::*;

#[test]
fn bindgen_test_layout_kvm_pic_state() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exact layout tests are generated by upstream, but they're now disconnected from upstream.

If we upgrade used upstream version, the values hardcoded here (generated with previous upstream version) might be stale.

If these tests would instead compare local structs against imported/upstream structs, the code here would not get stale and would work correctly for any upstream version.

@alexandruag
Copy link
Owner Author

Hmm, can't reply to the individual comments for some reason. Yeah, the staleness aspect is supposed to be addressed when changing the upstream version, and it wasn't captured here. One way of doing it that's aligned with your proposal is to include! the same autogenerated test code in two separate modules. The first exports (i.e. pub use ...) the local bindings, while the second does the same for the upstream bindings. This kind of approach seemed a bit ugly initially, but it's quite nice on second thought.

An example is definitely useful, but until one will be available just wanted to clarify the similarity aspect a bit. In both approaches there is are structs annotated with Versionize (or with hand written logic here and there), and this remains exactly the same. What's different with proxy is the extra conversion step that takes place before/after serialization/deserialization, and which is completely agnostic to serialization details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants