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

Fix AMD boot freeze #1015

Merged
merged 4 commits into from
Mar 20, 2019

Conversation

serban300
Copy link
Contributor

Issue #, if available: #815

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@serban300 serban300 self-assigned this Mar 15, 2019
@andreeaflorescu andreeaflorescu self-requested a review March 15, 2019 16:04
@serban300 serban300 added this to the AMD Support milestone Mar 18, 2019
@serban300 serban300 requested a review from dhrgit March 18, 2019 11:05
cpuid/src/common.rs Show resolved Hide resolved
use kvm::CpuId;
use kvm_bindings::kvm_cpuid_entry2;

const INTEL: &[u8; 12] = b"GenuineIntel";
Copy link
Member

Choose a reason for hiding this comment

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

nit: I would prefix these two with VENDOR_ID_* so we have a better understanding of what are we using them for.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

const INTEL: &[u8; 12] = b"GenuineIntel";
const AMD: &[u8; 12] = b"AuthenticAMD";

const EXT_FUNCTION: u32 = 0x80000000;
Copy link
Member

Choose a reason for hiding this comment

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

This is HIGHEST_EXTENDED_FUNCTION. We should name constants here as close as possible to the names that they have in the Intel/AMD manual so it is easier to correlate the code with the software development manuals.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

// this is safe because the host supports the `cpuid` instruction
let max_function = unsafe { __get_cpuid_max(function & EXT_FUNCTION).0 };
Copy link
Member

Choose a reason for hiding this comment

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

I am wondering if instead of always querying the maximum cpuid leaf supported, we can instead save the max_function somewhere and then just do the comparison from the following line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know if it's worth it. This is really fast. The entire call to filter_cpuid takes less then 15 microseconds

}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn get_cpuid(function: u32, count: u32) -> Result<CpuidResult, Error> {
Copy link
Member

Choose a reason for hiding this comment

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

nit: In Intel terminology I believe count is actually sub_leaf.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would keep count since it's named the same in multiple places, for example:

  1. Kernel
  2. GCC


#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn get_cpu_id_test() {
Copy link
Member

Choose a reason for hiding this comment

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

Please prefix test function with the word test so it is easier to mentally separate the tests from the helper functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -16,32 +16,28 @@ use std::arch::x86::__cpuid_count;
#[cfg(target_arch = "x86_64")]
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this commit (ba3d225) is just moving the code from the previous commit. I understand that the previous commit was already reviewed as part of the #882 PR, but other than that is there any reason for not squashing these two commits together?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no other reason. I squashed the commits.

@@ -3,6 +3,8 @@

// Basic CPUID Information
pub mod leaf_0x1 {
pub const FUNCTION: u32 = 0x1;
Copy link
Member

Choose a reason for hiding this comment

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

I know leaf and function are used interchangeably everywhere, but to not confuse people that look at this code for the first time, I would say to stick with only one name: either leaf or function.

If we want to still call them leaves, than we can rename FUNCTION to LEAF_NR. What do you say?

P.S. Nice improvement from having the values of the leaves hardcoded.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

cpuid/src/lib.rs Outdated
let cpuid_transformer: &dyn CpuidTransformer = match &vendor_id {
INTEL => &intel::IntelCpuidTransformer {},
AMD => &amd::AmdCpuidTransformer {},
_ => &other::OtherCpuidTransformer {},
Copy link
Member

Choose a reason for hiding this comment

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

What is the purpose of having OtherCpuidTransformer? When we add a new platform shouldn't we just add the corresponding transformer and not have a generic one here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it

@@ -66,7 +69,23 @@ pub enum Error {
VcpuCountOverflow,
/// The max size has been exceeded
SizeLimitExceeded,
/// A call to an internal helper method failed
InternalError(super::common::Error),
Copy link
Member

Choose a reason for hiding this comment

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

nit: use super::common and then just write common::Error here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I do this there will be conflicts between the Error defined here and common::Error. I will have to use self::Error instead of Error. I would keep it as it is

@serban300 serban300 force-pushed the serban300-amd branch 2 times, most recently from 37a38dc to 7b86f2e Compare March 18, 2019 16:27
@serban300
Copy link
Contributor Author

@andreeaflorescu I adressed all the comments. Please take another look.

@@ -3,6 +3,8 @@

// Basic CPUID Information
pub mod leaf_0x1 {
pub const LEAF_NR: u32 = 0x1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "nr" is not short for number. I suggest either "num" or "no".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -63,19 +65,41 @@ pub mod leaf_0x1 {
}
}

// Deterministic Cache Parameters Leaf
pub mod leaf_0x4 {
pub mod leaf_cache_parameters {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this node have a leaf number const as well?

Copy link
Member

Choose a reason for hiding this comment

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

I believe this is in fact a sub-leaf of the 0x4 leaf.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not an actual leaf. Since 0x4 and 0x8000_001d are very similar, but not completely the same (see ox4 - page 32 and 0x8000_001d - page 76 ), I created this "parent leaf" in order to hold the common properties. 0x4 and 0x8000_001d inherit these common properties, and then add their specific ones.

}

let cpuid2 = CpuId::from_entries(&entries);
*cpuid = cpuid2;
Copy link
Contributor

Choose a reason for hiding this comment

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

This function completely replaces the cpuid struct with a new copy, which is not something the caller might expect. I.e. if, in the future, we decide to extend the CpuId struct to hold more data, that data would be lost upon returning from this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created #1019 in order to address this issue. I would like to tackle it later with lower priority. Also see here: #1015 (comment)

) -> Result<(), Error> {
Ok(())
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I find this two-step logic (pre-process entry list + transform individual entries) a bit difficult to follow.

Why not have a single transforming / filtering trait, that ingests a CpuId struct and outputs a "fixed" CpuId? Then, different vendors / templates / whatever else might affect cpuid, could provide an implementation for that trait, and we could chain these transformations together, to produce the final emulated cpuid.

I.e. the flow I'm imagining looks something like this:

vcpu::configure()
  ...
  vendor_filter <- pick_vendor_filter_based_on_host_cpu()
  template_filter <- pick_template_filter_based_on_microvm_config()
  emulated_cpuid <-
    template_filter.apply(
      vendor_filter.apply(
        get_emulated_cpuid_from_kvm()
      )
    )
  ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In general I agree with having a single transforming / filtering trait, that ingests a CpuId struct and outputs a "fixed" CpuId. It seems like a more consistent approach, but I'm not sure it would be that easy. There are more things to take into account here. Like the fact that certain templates can be applied only to certain vendors. For example C3 can only be applied to Intel. I would like to dive deeper into it and take my time on implementing this. Also, since I have limited access to AMD hardware I would need this PR merged as soon as possible in order to continue the work on CPU feature masking for AMD.

So is it ok if I create another issue for this and address it probably next week ? We should also update the CPU templates in order to use the BitRange structure, I can do that as part of the same issue.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, then. We can take it offline and discuss this approach - I can't see many issues arising.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created #1025

Serban Iorga added 4 commits March 20, 2019 10:12
Signed-off-by: Serban Iorga <[email protected]>
@serban300
Copy link
Contributor Author

@dhrgit I answered to all the comments. Please take another look.

@andreeaflorescu andreeaflorescu merged commit 5e06e11 into firecracker-microvm:master Mar 20, 2019
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