Skip to content

fix(virtio/pci): map BARs only once - #2572

Open
cagatay-y wants to merge 1 commit into
hermit-os:mainfrom
cagatay-y:fix-bar-mapping
Open

fix(virtio/pci): map BARs only once#2572
cagatay-y wants to merge 1 commit into
hermit-os:mainfrom
cagatay-y:fix-bar-mapping

Conversation

@cagatay-y

Copy link
Copy Markdown
Contributor

It is possible for multiple capabilities to be accessed through the same base address, so only create a new mapping if one does not already exist for the BAR slot.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Benchmark Results

Details
Benchmark Current: f4bf447 Previous: 2e23902 Performance Ratio
startup_benchmark Build Time 82.71 s 80.34 s 1.03
startup_benchmark File Size 0.79 MB 0.80 MB 1.00
Startup Time - 1 core 0.75 s (±0.02 s) 0.75 s (±0.02 s) 1.00
Startup Time - 2 cores 0.76 s (±0.02 s) 0.74 s (±0.02 s) 1.03
Startup Time - 4 cores 0.77 s (±0.03 s) 0.74 s (±0.02 s) 1.04
multithreaded_benchmark Build Time 85.33 s 82.11 s 1.04
multithreaded_benchmark File Size 0.90 MB 0.86 MB 1.05
Multithreaded Pi Efficiency - 2 Threads 85.71 % (±7.07 %) 85.89 % (±6.61 %) 1.00
Multithreaded Pi Efficiency - 4 Threads 43.52 % (±2.12 %) 43.43 % (±2.56 %) 1.00
Multithreaded Pi Efficiency - 8 Threads 25.07 % (±1.71 %) 25.76 % (±1.53 %) 0.97
micro_benchmarks Build Time 85.08 s 80.40 s 1.06
micro_benchmarks File Size 0.90 MB 0.86 MB 1.05
Scheduling time - 1 thread 66.56 ticks (±3.66 ticks) 62.65 ticks (±4.06 ticks) 1.06
Scheduling time - 2 threads 35.76 ticks (±3.30 ticks) 34.08 ticks (±4.10 ticks) 1.05
Micro - Time for syscall (getpid) 4.14 ticks (±0.59 ticks) 3.45 ticks (±0.58 ticks) 1.20
Memcpy speed - (built_in) block size 4096 82675.68 MByte/s (±57144.17 MByte/s) 82448.38 MByte/s (±56997.13 MByte/s) 1.00
Memcpy speed - (built_in) block size 1048576 30269.24 MByte/s (±24475.78 MByte/s) 30585.98 MByte/s (±24707.84 MByte/s) 0.99
Memcpy speed - (built_in) block size 16777216 26463.51 MByte/s (±21820.32 MByte/s) 26340.06 MByte/s (±21720.96 MByte/s) 1.00
Memset speed - (built_in) block size 4096 82690.56 MByte/s (±57155.46 MByte/s) 82292.76 MByte/s (±56891.50 MByte/s) 1.00
Memset speed - (built_in) block size 1048576 31020.09 MByte/s (±24914.08 MByte/s) 31323.85 MByte/s (±25145.86 MByte/s) 0.99
Memset speed - (built_in) block size 16777216 27233.65 MByte/s (±22312.23 MByte/s) 27104.68 MByte/s (±22209.94 MByte/s) 1.00
Memcpy speed - (rust) block size 4096 75402.71 MByte/s (±52376.81 MByte/s) 74097.96 MByte/s (±51811.44 MByte/s) 1.02
Memcpy speed - (rust) block size 1048576 30319.22 MByte/s (±24519.97 MByte/s) 30361.60 MByte/s (±24602.37 MByte/s) 1.00
Memcpy speed - (rust) block size 16777216 27337.15 MByte/s (±22570.80 MByte/s) 27625.34 MByte/s (±22806.88 MByte/s) 0.99
Memset speed - (rust) block size 4096 75825.54 MByte/s (±52675.82 MByte/s) 74373.47 MByte/s (±51976.48 MByte/s) 1.02
Memset speed - (rust) block size 1048576 31062.45 MByte/s (±24951.21 MByte/s) 31110.89 MByte/s (±25033.24 MByte/s) 1.00
Memset speed - (rust) block size 16777216 28077.95 MByte/s (±23015.34 MByte/s) 28386.93 MByte/s (±23265.03 MByte/s) 0.99
alloc_benchmarks Build Time 78.12 s 74.76 s 1.04
alloc_benchmarks File Size 0.87 MB 0.87 MB 1.00
Allocations - Allocation success 91.31 % 91.31 % 1
Allocations - Deallocation success 100.00 % 100.00 % 1
Allocations - Pre-fail Allocations 61.44 % 61.44 % 1
Allocations - Average Allocation time 4191.09 Ticks (±131.70 Ticks) 5860.58 Ticks (±98.43 Ticks) 0.72
Allocations - Average Allocation time (no fail) 4966.01 Ticks (±133.76 Ticks) 6554.81 Ticks (±92.86 Ticks) 0.76
Allocations - Average Deallocation time 859.81 Ticks (±135.24 Ticks) 1805.01 Ticks (±250.35 Ticks) 0.48
mutex_benchmark Build Time 81.62 s 79.82 s 1.02
mutex_benchmark File Size 0.90 MB 0.86 MB 1.05
Mutex Stress Test Average Time per Iteration - 1 Threads 12.22 ns (±0.88 ns) 12.10 ns (±0.41 ns) 1.01
Mutex Stress Test Average Time per Iteration - 2 Threads 41.08 ns (±2.15 ns) 40.26 ns (±1.68 ns) 1.02

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LLM review

The changes introduce a caching mechanism for PCI BAR mappings using LazyCell, but there are potential correctness and safety issues to address.

Comment thread src/drivers/virtio/transport/pci.rs Outdated
Comment thread src/drivers/virtio/transport/pci.rs Outdated
Comment thread src/drivers/virtio/transport/pci.rs Outdated
Comment thread src/drivers/virtio/transport/pci.rs
@stlankes

stlankes commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@cagatay-y @mkroening @jounathaen Sorry, this PR was my test if an LLM is able to support reviews. I am not sure, if this helps...

@cagatay-y

Copy link
Copy Markdown
Contributor Author

Sorry, this PR was my test if an LLM is able to support reviews. I am not sure, if this helps...

I replied to the comments in case it is useful for the evaluation of the test :)

@mkroening mkroening left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! :)

Comment thread src/drivers/virtio/transport/pci.rs Outdated
@mkroening mkroening self-assigned this Jul 29, 2026
Comment thread src/drivers/virtio/transport/pci.rs Outdated

@mkroening mkroening left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The CI failure does not seem to be spurious. I am wondering what goes wrong exactly. Maybe we need to go back to lazy cells after all. What kind of issue does this PR resolve? Is there a bug, or is this just to save resources?

By the way: where does the number six come from?

@cagatay-y

Copy link
Copy Markdown
Contributor Author

The CI failure does not seem to be spurious. I am wondering what goes wrong exactly.

The issue is that if a base address is 64 bits, it occupies two slots, so the slot after it is invalid as a BAR by itself. When we blindly try to map the slot that is occupied by the second part of the previous BAR, it returns values that cannot be interpreted as a BAR. There does not seem to be a way to tell that it is not a BAR by itself without reading the previous one and skipping the current one accordingly.

Maybe we need to go back to lazy cells after all.

We can either switch back to lazy cells or solve the issue by iterating over the slot numbers and skipping one if the previous slot was a 64-bit one. That would require changing the current memory_map_bar implementation, though, as it does not provide the BAR type information to the caller. Which approach would you prefer?

What kind of issue does this PR resolve? Is there a bug, or is this just to save resources?

I did not encounter an issue caused by the current behavior but mapping the same address twice seemed like an accident to me.

By the way: where does the number six come from?

Per PCIe spec. sec. 7.5.1.2.1 "A Type 0 Configuration Space Header has six DWORD locations allocated for Base Address registers starting at offset 10h in Configuration Space." I should introduce a constant with a descriptive name for that.

By the way, do you think it would make sense to have the virtio-console test later amongst the virtio tests? For the common virtio issues it could be nice to have the tests where the console output is not affected earlier, so that the CI output is more helpful.

It is possible for multiple capabilities to be accessed through the same base
address, so create the mappings for the BAR slots at the beginning and use saved
virtual addresses.
.into_iter()
.find_map(|bar| {
bar.map(|(addr, _size)| unsafe {
VolatileRef::new(NonNull::new(addr.as_mut_ptr()).unwrap())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The behaviour is slightly different:

@cagatay-y
cagatay-y requested a review from mkroening July 31, 2026 15:58
@mkroening

Copy link
Copy Markdown
Member

We can either switch back to lazy cells or solve the issue by iterating over the slot numbers and skipping one if the previous slot was a 64-bit one. That would require changing the current memory_map_bar implementation, though, as it does not provide the BAR type information to the caller. Which approach would you prefer?

Let's go lazy, then. ^^

By the way, do you think it would make sense to have the virtio-console test later amongst the virtio tests? For the common virtio issues it could be nice to have the tests where the console output is not affected earlier, so that the CI output is more helpful.

I did originally put them to the non-virtio stdin test, but your reasoning makes sense. Feel free to submit a PR. 👍

@cagatay-y

Copy link
Copy Markdown
Contributor Author

Let's go lazy, then. ^^

I've realized that rather than returning the BAR type information to the caller, we can move the array construction into the mapping function. The latest commit does that. Should I revert to lazy cells or is the current version good?

I've also left a review comment about a change in the RTL8139 driver's behavior if we end up going with the eager approach.

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