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

Multi region packing #1599

Merged
merged 22 commits into from Feb 5, 2024
Merged

Multi region packing #1599

merged 22 commits into from Feb 5, 2024

Conversation

mkeeter
Copy link
Collaborator

@mkeeter mkeeter commented Jan 26, 2024

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 the gimlet-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 the kconfig.

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

before

After

after

@mkeeter mkeeter requested a review from cbiffle January 26, 2024 20:07
@mkeeter mkeeter force-pushed the multi-region-packing branch 2 times, most recently from 6c77ace to 86e60eb Compare January 29, 2024 16:00
Copy link
Collaborator

@cbiffle cbiffle left a 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).

@@ -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);
Copy link
Collaborator

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.)

Copy link
Collaborator Author

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.

@@ -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);
Copy link
Collaborator

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.

Copy link
Collaborator Author

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 by MultiRegionConfig::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

@@ -1311,8 +1338,8 @@ fn generate_task_linker_script(

writeln!(linkscr, "MEMORY\n{{")?;
for (name, range) in map {
Copy link
Collaborator

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup, done in 05e21fb

@@ -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>>>,
Copy link
Collaborator

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.

Copy link
Collaborator Author

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)

/// 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>>>>,
Copy link
Collaborator

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?)

Copy link
Collaborator Author

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)

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 {
Copy link
Collaborator

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)

Copy link
Collaborator Author

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

@@ -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];
Copy link
Collaborator

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"

Copy link
Collaborator Author

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

@cbiffle
Copy link
Collaborator

cbiffle commented Jan 29, 2024

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.

build/xtask/src/dist.rs Outdated Show resolved Hide resolved
build/xtask/src/dist.rs Outdated Show resolved Hide resolved
@mkeeter mkeeter merged commit eb8e539 into master Feb 5, 2024
83 checks passed
@mkeeter mkeeter deleted the multi-region-packing branch February 5, 2024 21:25
@mkeeter mkeeter mentioned this pull request Mar 20, 2024
mkeeter added a commit that referenced this pull request Mar 20, 2024
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.
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.

None yet

2 participants