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
Multi region packing #1599
Multi region packing #1599
Conversation
6c77ace
to
86e60eb
Compare
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 generally looks good. There are a few areas where I think there's an unmentioned assumption about ordering where I'd like to see some docs (or a type that causes the assumption to be explicit).
build/xtask/src/sizes.rs
Outdated
@@ -78,7 +79,10 @@ pub fn run( | |||
} | |||
let size = toml.kernel.requires[&mem.to_string()]; | |||
|
|||
let suggestion = toml.suggest_memory_region_size("kernel", used); | |||
let suggestion = toml.suggest_memory_region_size("kernel", used, 1); | |||
assert_eq!(suggestion.len(), 1); |
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.
So imagine I'm here in the future and this assertion has failed. What should I look at? Asking because I don't totally understand it as a reviewer. (The answer would make a good assert message or comment.)
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.
Added a string to the assertion in 7d2d02b; this detects a failure in suggest_memory_region_size
where it has suggested > 1 region for the kernel, which should never happen.
sys/kern/build.rs
Outdated
@@ -292,7 +303,7 @@ fn translate_address( | |||
task_index: usize, | |||
address: OwnedAddress, | |||
) -> u32 { | |||
let key = RegionKey::Owned(task_index, address.region_name); | |||
let key = RegionKey::Owned(task_index, 0, address.region_name); |
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 this right? It seems simpler than I'd expect.
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, although it's a little subtle:
- Regions are populated in order from the
MultiRegionConfig
, so region 0 is going to be the first region in memory (at an address given byMultiRegionConfig::base
) - Regions are contiguous, so the offset can be taken from the first region's base address
I added a comment to that effect in 122b0a1
build/xtask/src/dist.rs
Outdated
@@ -1311,8 +1338,8 @@ fn generate_task_linker_script( | |||
|
|||
writeln!(linkscr, "MEMORY\n{{")?; | |||
for (name, range) in map { |
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 this'd be clearer if the name became ranges
, since it's now more than one range.
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.
Yup, done in 05e21fb
build/xtask/src/dist.rs
Outdated
@@ -1290,7 +1317,7 @@ fn check_task_priorities(toml: &Config) -> Result<()> { | |||
|
|||
fn generate_task_linker_script( | |||
name: &str, | |||
map: &BTreeMap<String, Range<u32>>, | |||
map: &BTreeMap<String, Vec<Range<u32>>>, |
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.
Does the code below assume this vec is sorted? I notice it accesses first and last.
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 as below, fixed with ContiguousRanges
(3d128cb)
build/xtask/src/dist.rs
Outdated
/// A task may have multiple address ranges in the same memory space for | ||
/// efficient packing; if this is the case, the addresses will be contiguous | ||
/// and each individual range will respect MPU requirements. | ||
pub tasks: BTreeMap<String, BTreeMap<String, Vec<Range<u32>>>>, |
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 this has an implicit assumption about vec order as well, it should go in the comment. Or maybe a BTreeSet would be better than a 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.
Yup, I added a ContinguousRanges
type which enforces that the ranges are contiguous (3d128cb)
build/xtask/src/dist.rs
Outdated
if let Some(r) = tasks[name].max_sizes.get(&mem.to_string()) { | ||
if bytes > *r as u64 { | ||
let total_bytes = bytes.iter().sum::<u64>(); | ||
if total_bytes > *r as u64 { |
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: this looks like a lossy conversion but isn't, consider u64::from(*r)
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 catch, done in 3e2421a
build/xtask/src/dist.rs
Outdated
@@ -2082,7 +2248,7 @@ pub fn make_kconfig( | |||
for (i, (name, task)) in toml.tasks.iter().enumerate() { | |||
let stacksize = task.stacksize.or(toml.stacksize).unwrap(); | |||
|
|||
let flash = &task_allocations[name]["flash"]; | |||
let flash = &task_allocations[name]["flash"][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.
I kinda feel like every case where we're explicitly accessing the first element of the list should get a comment, because each time I stop and go "uh... wait... is this right or making an assumption"
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.
Yup, fixed with ContiguousRanges
in 3d128cb
I asked @mkeeter for time measurements of the potentially N**2 algorithm here -- execution is currently under a millisecond in every case. So, not worth attempting to optimize the complexity, imo. |
3dc24d8
to
f2bebca
Compare
The kernel will reject leases that span multiple regions, so this PR disables smart packing entirely by forcing each task's flash into a single MPU region (just like before #1599). Otherwise, tasks which loan out their flash memory may produce spurious faults.
This PR modifies the build system to let flash use all available MPU regions, resulting in tighter packing and smaller total image size. This shrinks the
sidecar-rev-c
image by 256 KiB, and thegimlet-f
image by 188 KiB.In other words, tasks go from having exactly 1 flash region to at least 1 flash region; this is mostly plumbing that change from
suggest_memory_region_size
all the way through thekconfig
.The change does makes task packing trickier, because tasks can be placed in one of two orientations: largest-chunk-first, or largest-chunk-last. I went for a very dumb O(N^2) algorithm that checks every unplaced task and picks the best; we're far from having > 100 tasks, so I'm not worried about bad scaling (famous last words!).
In addition, it updates
xtask/src/sizes.rs
to print the individual chunks when the-v
flag is specified.I'm expecting CI to get mad, because some kernels may need more flash space to hold the extra regions.
Before
After