-
Notifications
You must be signed in to change notification settings - Fork 123
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
Conversation
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.
vm/vmcore/guestmem/src/lib.rs
Outdated
@@ -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, |
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 feels really easy to forget. Is there some way we could do this automatically with a drop guard or something?
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.
No. The bitmap is controlled completely by the implementation of GuestMemoryAccess
.
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.
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.
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.
What would be the implications of a caller reducing access and forgetting to sync?
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.
There might be callers accessing memory using the old permissions for a short period. Which is probably never OK.
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.
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?
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 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.
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.
Worth filing an issue on yourself to track?
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.
We similarly have to remember to call run before trying to read the bitmap, right? Since it's quiesced on run_vp.
vm/vmcore/guestmem/src/lib.rs
Outdated
} | ||
} | ||
} | ||
} | ||
}; | ||
// If the `rcu` feature is enabled, run the function in an RCU critical |
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.
// 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); |
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.
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.
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 don't see a way to do that.
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.
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?
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 don't think this is any more or less safe as a concrete type.
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 guess, it makes me uneasy that there's absolutely nothing backing up or asserting the safety comment though.
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.