Skip to content

underhill_mem: add RCU implementation to synchronize bitmap changes #1383

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

Merged
merged 8 commits into from
May 20, 2025

Conversation

jstarks
Copy link
Member

@jstarks jstarks commented May 19, 2025

When guest memory page protections are changed (e.g., pages are transitioned between shared and private), we need to flush concurrent accesses to those pages by the paravisor before updating the page state in hardware. Otherwise, faults or cross-VTL data leaks may occur.

Add this synchronization as cheaply as we can: add a simple RCU (Read-Copy-Update) mechanism that allows threads accessing guest memory to cheaply synchronize with threads mutating the page access bitmaps. Use the membarrier() syscall on Linux to allow readers to operate without memory barriers, shifting the expensive to the (infrequent) bitmap update paths.

Only enable this mechanism in OpenHCL, since other environments do not rely on bitmap-based guest memory access controls.

When guest memory page protections are changed (e.g., pages are
transitioned between shared and private), we need to flush concurrent
accesses to those pages by the paravisor before updating the page state
in hardware. Otherwise, faults or cross-VTL data leaks may occur.

Add this synchronization as cheaply as we can: add a simple RCU (Read-Copy-Update)
mechanism that allows threads accessing guest memory to cheaply synchronize
with threads mutating the page access bitmaps. Use the membarrier()
syscall on Linux to allow readers to operate without memory barriers,
shifting the expensive to the (infrequent) bitmap update paths.

Only enable this mechanism in OpenHCL, since other environments do not
rely on bitmap-based guest memory access controls.
@jstarks jstarks requested review from a team as code owners May 19, 2025 23:25
@jstarks jstarks requested a review from sluck-msft May 19, 2025 23:25
@@ -328,7 +327,12 @@ pub unsafe trait GuestMemoryAccess: 'static + Send + Sync {
/// fails, then the associated `*_fallback` routine is called to handle the
/// error.
///
/// TODO: add a synchronization scheme.
/// Bitmap checks are performed under the [`minircu::global()`] RCU domain,
Copy link
Contributor

@smalis-msft smalis-msft May 20, 2025

Choose a reason for hiding this comment

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

This feels really easy to forget. Is there some way we could do this automatically with a drop guard or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. The bitmap is controlled completely by the implementation of GuestMemoryAccess.

Copy link
Member Author

Choose a reason for hiding this comment

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

And you don't have to flush after every change, just changes that reduce access. And you probably want to try to batch things. So I don't think there's much we can do here.

Copy link
Contributor

Choose a reason for hiding this comment

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

What would be the implications of a caller reducing access and forgetting to sync?

Copy link
Member Author

Choose a reason for hiding this comment

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

There might be callers accessing memory using the old permissions for a short period. Which is probably never OK.

Copy link
Contributor

Choose a reason for hiding this comment

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

All the more reason we should try to make this harder to forget then. Is there really no way we could create some sort of write guard or drop guard or something around the relevent methods? Could we have a way to err on the side of oversyncing with some way to opt-out when it's not needed instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think to do that, we'd probably have to move all the bitmap manipulation code into this crate. We'd still want to let the implementor control the allocation of the bitmap, though, since we might want it to come from non-kernel-managed memory, etc. And we'd have to create some guards for batched updates, yes. The guard could track if you actually restricted a bitmap and only flush the RCU in that case (and provide some kind of "I know what I'm doing" override).

I'd be open to such a change, but I think it's beyond the scope of what I want to do here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Worth filing an issue on yourself to track?

Copy link
Contributor

@sluck-msft sluck-msft May 20, 2025

Choose a reason for hiding this comment

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

We similarly have to remember to call run before trying to read the bitmap, right? Since it's quiesced on run_vp.

}
}
}
}
};
// If the `rcu` feature is enabled, run the function in an RCU critical
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// If the `rcu` feature is enabled, run the function in an RCU critical
// If the `bitmap` feature is enabled, run the function in an RCU critical

}

/// A pointer representing a valid reference to a value.
struct TlsRef<T>(*const T);
Copy link
Contributor

Choose a reason for hiding this comment

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

There should probably be some sort of bound on this type, as otherwise it could accidentally point to anything and the SAFETY comments might not hold.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't see a way to do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

The only type this is used to point to currently is an AtomicU64 right? Can we just make a wrapper type for that field and have TlsRef not be generic?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think this is any more or less safe as a concrete type.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess, it makes me uneasy that there's absolutely nothing backing up or asserting the safety comment though.

@jstarks jstarks merged commit 426b1ca into microsoft:main May 20, 2025
28 checks passed
@jstarks jstarks deleted the minircu branch May 20, 2025 20:41
@mebersol mebersol added the backport_2505 Change should be backported to the release/2505 branch label May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport_2505 Change should be backported to the release/2505 branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants