Skip to content

Commit 26475af

Browse files
committed
refactor: merge run_on_thread_local into signal handler
Merge run_on_thread_local into signal handler since it is only used there. The error returned from run_on_thread_local was ignored, so instead replace it with logic to only use vcpu ptr if TLS is initialized, without returning any errors. The reason for not asserting on TLS being initialized here is that during Firecracker shutdown, vcpus will be destroyed and TLS will be reset. If signal will be send to Firecracker during that time, the TLS accessed from a signal handler will be empty. But this is expected, so no assertions/panics are needed. Because Rust is a good language, it does not allow to reference TLS_VCPU_PTR definded inside impl block inside the signal_handler function. So move the TLS_VCPU_PTR definition outside the Vcpu impl block. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 30928aa commit 26475af

File tree

1 file changed

+13
-66
lines changed

1 file changed

+13
-66
lines changed

src/vmm/src/vstate/vcpu.rs

Lines changed: 13 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ pub enum CopyKvmFdError {
8888
CreateVcpuError(#[from] kvm_ioctls::Error),
8989
}
9090

91+
thread_local!(static TLS_VCPU_PTR: VcpuCell = const { Cell::new(None) });
92+
9193
/// A wrapper around creating and using a vcpu.
9294
#[derive(Debug)]
9395
pub struct Vcpu {
@@ -110,15 +112,13 @@ pub struct Vcpu {
110112
}
111113

112114
impl Vcpu {
113-
thread_local!(static TLS_VCPU_PTR: VcpuCell = const { Cell::new(None) });
114-
115115
/// Associates `self` with the current thread.
116116
///
117117
/// It is a prerequisite to successfully run `init_thread_local_data()` before using
118118
/// `run_on_thread_local()` on the current thread.
119119
/// This function will panic if there already is a `Vcpu` present in the TLS.
120120
fn init_thread_local_data(&mut self) {
121-
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| {
121+
TLS_VCPU_PTR.with(|cell: &VcpuCell| {
122122
assert!(cell.get().is_none());
123123
cell.set(Some(self as *mut Vcpu));
124124
})
@@ -130,39 +130,12 @@ impl Vcpu {
130130
fn reset_thread_local_data(&mut self) {
131131
// Best-effort to clean up TLS. If the `Vcpu` was moved to another thread
132132
// _before_ running this, then there is nothing we can do.
133-
Self::TLS_VCPU_PTR.with(|cell| {
133+
TLS_VCPU_PTR.with(|cell| {
134134
let vcpu_ptr = cell.get().unwrap();
135135
assert!(std::ptr::eq(vcpu_ptr, self));
136136
// Have to do this trick because `update` method is
137137
// not stable on Cell.
138-
Self::TLS_VCPU_PTR.with(|cell| cell.take());
139-
})
140-
}
141-
142-
/// Runs `func` for the `Vcpu` associated with the current thread.
143-
///
144-
/// It requires that `init_thread_local_data()` was run on this thread.
145-
///
146-
/// Fails if there is no `Vcpu` associated with the current thread.
147-
///
148-
/// # Safety
149-
///
150-
/// This is marked unsafe as it allows temporary aliasing through
151-
/// dereferencing from pointer an already borrowed `Vcpu`.
152-
unsafe fn run_on_thread_local<F>(func: F) -> Result<(), VcpuError>
153-
where
154-
F: FnOnce(&mut Vcpu),
155-
{
156-
Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| {
157-
if let Some(vcpu_ptr) = cell.get() {
158-
// SAFETY: Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty,
159-
// and it is being cleared on `Vcpu::drop` so there is no dangling pointer.
160-
let vcpu_ref = unsafe { &mut *vcpu_ptr };
161-
func(vcpu_ref);
162-
Ok(())
163-
} else {
164-
Err(VcpuError::VcpuTlsNotPresent)
165-
}
138+
TLS_VCPU_PTR.with(|cell| cell.take());
166139
})
167140
}
168141

@@ -172,14 +145,16 @@ impl Vcpu {
172145
self.init_thread_local_data();
173146

174147
extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
175-
// SAFETY: This is safe because it's temporarily aliasing the `Vcpu` object, but we are
176-
// only reading `vcpu.fd` which does not change for the lifetime of the `Vcpu`.
177-
unsafe {
178-
let _ = Vcpu::run_on_thread_local(|vcpu| {
148+
TLS_VCPU_PTR.with(|cell: &VcpuCell| {
149+
if let Some(vcpu_ptr) = cell.get() {
150+
// SAFETY: Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty,
151+
// and it is being cleared on `Vcpu::drop` so there is no dangling pointer.
152+
let vcpu = unsafe { &mut *vcpu_ptr };
153+
179154
vcpu.kvm_vcpu.fd.set_kvm_immediate_exit(1);
180155
fence(Ordering::Release);
181-
});
182-
}
156+
}
157+
})
183158
}
184159

185160
register_signal_handler(sigrtmin() + VCPU_RTSIG_OFFSET, handle_signal)
@@ -1014,34 +989,6 @@ pub(crate) mod tests {
1014989
assert!(vcpu.kvm_vcpu.peripherals.mmio_bus.is_some());
1015990
}
1016991

1017-
#[test]
1018-
fn test_vcpu_tls() {
1019-
let (_, _, mut vcpu) = setup_vcpu(0x1000);
1020-
1021-
// Running on the TLS vcpu should fail before we actually initialize it.
1022-
unsafe {
1023-
Vcpu::run_on_thread_local(|_| ()).unwrap_err();
1024-
}
1025-
1026-
// Initialize vcpu TLS.
1027-
vcpu.init_thread_local_data();
1028-
1029-
// Validate TLS vcpu is the local vcpu by changing the `id` then validating against
1030-
// the one in TLS.
1031-
vcpu.kvm_vcpu.index = 12;
1032-
unsafe {
1033-
Vcpu::run_on_thread_local(|v| assert_eq!(v.kvm_vcpu.index, 12)).unwrap();
1034-
}
1035-
1036-
// Reset vcpu TLS.
1037-
vcpu.reset_thread_local_data();
1038-
1039-
// Running on the TLS vcpu after TLS reset should fail.
1040-
unsafe {
1041-
Vcpu::run_on_thread_local(|_| ()).unwrap_err();
1042-
}
1043-
}
1044-
1045992
#[test]
1046993
#[should_panic]
1047994
fn test_tls_double_init() {

0 commit comments

Comments
 (0)