Skip to content

Commit 2182bc2

Browse files
committed
refactor: assert on vcpu TLS set and use
The logic for setting and using vcpu TLS is Firecacker internal and critical to Firecracker function properly. Additionally, errors returned by TLS touching code was always ignored, making these errors useless. In addition, remove the TLS reset function, since Firecracker does not support vcpu movement across different threads. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent a68bf2b commit 2182bc2

File tree

1 file changed

+23
-68
lines changed

1 file changed

+23
-68
lines changed

src/vmm/src/vstate/vcpu.rs

Lines changed: 23 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ pub enum VcpuError {
5151
VcpuResponse(KvmVcpuError),
5252
/// Cannot spawn a new vCPU thread: {0}
5353
VcpuSpawn(io::Error),
54-
/// Cannot clean init vcpu TLS
55-
VcpuTlsInit,
56-
/// Vcpu not present in TLS
57-
VcpuTlsNotPresent,
5854
/// Error with gdb request sent
5955
#[cfg(feature = "gdb")]
6056
GdbRequest(GdbTargetError),
@@ -119,33 +115,10 @@ impl Vcpu {
119115
/// It is a prerequisite to successfully run `init_thread_local_data()` before using
120116
/// `run_on_thread_local()` on the current thread.
121117
/// This function will return an error if there already is a `Vcpu` present in the TLS.
122-
fn init_thread_local_data(&mut self) -> Result<(), VcpuError> {
118+
fn init_thread_local_data(&mut self) {
123119
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| {
124-
if cell.get().is_some() {
125-
return Err(VcpuError::VcpuTlsInit);
126-
}
120+
assert!(cell.get().is_none());
127121
cell.set(Some(self as *mut Vcpu));
128-
Ok(())
129-
})
130-
}
131-
132-
/// Deassociates `self` from the current thread.
133-
///
134-
/// Should be called if the current `self` had called `init_thread_local_data()` and
135-
/// now needs to move to a different thread.
136-
///
137-
/// Fails if `self` was not previously associated with the current thread.
138-
fn reset_thread_local_data(&mut self) -> Result<(), VcpuError> {
139-
// Best-effort to clean up TLS. If the `Vcpu` was moved to another thread
140-
// _before_ running this, then there is nothing we can do.
141-
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| {
142-
if let Some(vcpu_ptr) = cell.get() {
143-
if std::ptr::eq(vcpu_ptr, self) {
144-
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| cell.take());
145-
return Ok(());
146-
}
147-
}
148-
Err(VcpuError::VcpuTlsNotPresent)
149122
})
150123
}
151124

@@ -159,20 +132,15 @@ impl Vcpu {
159132
///
160133
/// This is marked unsafe as it allows temporary aliasing through
161134
/// dereferencing from pointer an already borrowed `Vcpu`.
162-
unsafe fn run_on_thread_local<F>(func: F) -> Result<(), VcpuError>
135+
unsafe fn run_on_thread_local<F>(func: F)
163136
where
164137
F: FnOnce(&mut Vcpu),
165138
{
166139
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| {
167-
if let Some(vcpu_ptr) = cell.get() {
168-
// SAFETY: Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty,
169-
// and it is being cleared on `Vcpu::drop` so there is no dangling pointer.
170-
let vcpu_ref = unsafe { &mut *vcpu_ptr };
171-
func(vcpu_ref);
172-
Ok(())
173-
} else {
174-
Err(VcpuError::VcpuTlsNotPresent)
175-
}
140+
let vcpu_ptr = cell.get().unwrap();
141+
// SAFETY: Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty.
142+
let vcpu_ref = unsafe { &mut *vcpu_ptr };
143+
func(vcpu_ref);
176144
})
177145
}
178146

@@ -183,7 +151,7 @@ impl Vcpu {
183151
// SAFETY: This is safe because it's temporarily aliasing the `Vcpu` object, but we are
184152
// only reading `vcpu.fd` which does not change for the lifetime of the `Vcpu`.
185153
unsafe {
186-
let _ = Vcpu::run_on_thread_local(|vcpu| {
154+
Vcpu::run_on_thread_local(|vcpu| {
187155
vcpu.kvm_vcpu.fd.set_kvm_immediate_exit(1);
188156
fence(Ordering::Release);
189157
});
@@ -254,8 +222,7 @@ impl Vcpu {
254222
.name(format!("fc_vcpu {}", self.kvm_vcpu.index))
255223
.spawn(move || {
256224
let filter = &*seccomp_filter;
257-
self.init_thread_local_data()
258-
.expect("Cannot cleanly initialize vcpu TLS.");
225+
self.init_thread_local_data();
259226
// Synchronization to make sure thread local data is initialized.
260227
barrier.wait();
261228
self.run(filter);
@@ -617,12 +584,6 @@ fn handle_kvm_exit(
617584
}
618585
}
619586

620-
impl Drop for Vcpu {
621-
fn drop(&mut self) {
622-
let _ = self.reset_thread_local_data();
623-
}
624-
}
625-
626587
/// List of events that the Vcpu can receive.
627588
#[derive(Debug, Clone)]
628589
pub enum VcpuEvent {
@@ -1025,43 +986,37 @@ pub(crate) mod tests {
1025986
}
1026987

1027988
#[test]
1028-
fn test_vcpu_tls() {
1029-
let (_, _, mut vcpu) = setup_vcpu(0x1000);
1030-
989+
#[should_panic]
990+
fn test_vcpu_tls_not_set_panic() {
1031991
// Running on the TLS vcpu should fail before we actually initialize it.
1032992
unsafe {
1033-
Vcpu::run_on_thread_local(|_| ()).unwrap_err();
993+
Vcpu::run_on_thread_local(|_| ());
1034994
}
995+
}
996+
997+
#[test]
998+
fn test_vcpu_tls_set() {
999+
let (_, _, mut vcpu) = setup_vcpu(0x1000);
10351000

10361001
// Initialize vcpu TLS.
1037-
vcpu.init_thread_local_data().unwrap();
1002+
vcpu.init_thread_local_data();
10381003

10391004
// Validate TLS vcpu is the local vcpu by changing the `id` then validating against
10401005
// the one in TLS.
10411006
vcpu.kvm_vcpu.index = 12;
10421007
unsafe {
1043-
Vcpu::run_on_thread_local(|v| assert_eq!(v.kvm_vcpu.index, 12)).unwrap();
1044-
}
1045-
1046-
// Reset vcpu TLS.
1047-
vcpu.reset_thread_local_data().unwrap();
1048-
1049-
// Running on the TLS vcpu after TLS reset should fail.
1050-
unsafe {
1051-
Vcpu::run_on_thread_local(|_| ()).unwrap_err();
1008+
Vcpu::run_on_thread_local(|v| assert_eq!(v.kvm_vcpu.index, 12));
10521009
}
1053-
1054-
// Second reset should return error.
1055-
vcpu.reset_thread_local_data().unwrap_err();
10561010
}
10571011

10581012
#[test]
1013+
#[should_panic]
10591014
fn test_invalid_tls() {
10601015
let (_, _, mut vcpu) = setup_vcpu(0x1000);
10611016
// Initialize vcpu TLS.
1062-
vcpu.init_thread_local_data().unwrap();
1017+
vcpu.init_thread_local_data();
10631018
// Trying to initialize non-empty TLS should error.
1064-
vcpu.init_thread_local_data().unwrap_err();
1019+
vcpu.init_thread_local_data();
10651020
}
10661021

10671022
#[test]
@@ -1080,7 +1035,7 @@ pub(crate) mod tests {
10801035
let handle = std::thread::Builder::new()
10811036
.name("test_vcpu_kick".to_string())
10821037
.spawn(move || {
1083-
vcpu.init_thread_local_data().unwrap();
1038+
vcpu.init_thread_local_data();
10841039
// Notify TLS was populated.
10851040
vcpu_barrier.wait();
10861041
// Loop for max 1 second to check if the signal handler has run.

0 commit comments

Comments
 (0)