vmm: Make gdb break/resuming more resilient

When starting the VM such that it is already on a breakpoint (via
stop_on_boot) when attached to gdb then start the vCPUs in a paused
state rather than starting the vCPUs later (upon resume).

Further, make the resumption/break of the VM more resilient by only
attempting to resume the vCPUs if were are already in a break point and
only attempting to pause/break if we were already running.

Fixes: #4354

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2022-07-25 12:34:46 +01:00 committed by Sebastien Boeuf
parent 23352c4945
commit 0e29379bcf
2 changed files with 30 additions and 46 deletions

View File

@ -1048,7 +1048,12 @@ impl CpuManager {
} }
/// Start up as many vCPUs threads as needed to reach `desired_vcpus` /// Start up as many vCPUs threads as needed to reach `desired_vcpus`
fn activate_vcpus(&mut self, desired_vcpus: u8, inserting: bool) -> Result<()> { fn activate_vcpus(
&mut self,
desired_vcpus: u8,
inserting: bool,
paused: Option<bool>,
) -> Result<()> {
if desired_vcpus > self.config.max_vcpus { if desired_vcpus > self.config.max_vcpus {
return Err(Error::DesiredVCpuCountExceedsMax); return Err(Error::DesiredVCpuCountExceedsMax);
} }
@ -1057,11 +1062,16 @@ impl CpuManager {
(desired_vcpus - self.present_vcpus() + 1) as usize, (desired_vcpus - self.present_vcpus() + 1) as usize,
)); ));
if let Some(paused) = paused {
self.vcpus_pause_signalled.store(paused, Ordering::SeqCst);
}
info!( info!(
"Starting vCPUs: desired = {}, allocated = {}, present = {}", "Starting vCPUs: desired = {}, allocated = {}, present = {}, paused = {}",
desired_vcpus, desired_vcpus,
self.vcpus.len(), self.vcpus.len(),
self.present_vcpus() self.present_vcpus(),
self.vcpus_pause_signalled.load(Ordering::SeqCst)
); );
// This reuses any inactive vCPUs as well as any that were newly created // This reuses any inactive vCPUs as well as any that were newly created
@ -1101,26 +1111,16 @@ impl CpuManager {
} }
// Starts all the vCPUs that the VM is booting with. Blocks until all vCPUs are running. // Starts all the vCPUs that the VM is booting with. Blocks until all vCPUs are running.
pub fn start_boot_vcpus(&mut self) -> Result<()> { pub fn start_boot_vcpus(&mut self, paused: bool) -> Result<()> {
self.activate_vcpus(self.boot_vcpus(), false) self.activate_vcpus(self.boot_vcpus(), false, Some(paused))
} }
pub fn start_restored_vcpus(&mut self) -> Result<()> { pub fn start_restored_vcpus(&mut self) -> Result<()> {
let vcpu_numbers = self.vcpus.len() as u8; self.activate_vcpus(self.vcpus.len() as u8, false, Some(true))
let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_numbers + 1) as usize));
// Restore the vCPUs in "paused" state.
self.vcpus_pause_signalled.store(true, Ordering::SeqCst);
for vcpu_id in 0..vcpu_numbers {
let vcpu = Arc::clone(&self.vcpus[vcpu_id as usize]);
self.start_vcpu(vcpu, vcpu_id, vcpu_thread_barrier.clone(), false)
.map_err(|e| { .map_err(|e| {
Error::StartRestoreVcpu(anyhow!("Failed to start restored vCPUs: {:#?}", e)) Error::StartRestoreVcpu(anyhow!("Failed to start restored vCPUs: {:#?}", e))
})?; })?;
}
// Unblock all restored CPU threads.
vcpu_thread_barrier.wait();
Ok(()) Ok(())
} }
@ -1136,7 +1136,7 @@ impl CpuManager {
match desired_vcpus.cmp(&self.present_vcpus()) { match desired_vcpus.cmp(&self.present_vcpus()) {
cmp::Ordering::Greater => { cmp::Ordering::Greater => {
self.create_vcpus(desired_vcpus, None)?; self.create_vcpus(desired_vcpus, None)?;
self.activate_vcpus(desired_vcpus, true)?; self.activate_vcpus(desired_vcpus, true, None)?;
Ok(true) Ok(true)
} }
cmp::Ordering::Less => { cmp::Ordering::Less => {

View File

@ -2143,13 +2143,11 @@ impl Vm {
self.vm.tdx_finalize().map_err(Error::FinalizeTdx)?; self.vm.tdx_finalize().map_err(Error::FinalizeTdx)?;
} }
if new_state == VmState::Running {
self.cpu_manager self.cpu_manager
.lock() .lock()
.unwrap() .unwrap()
.start_boot_vcpus() .start_boot_vcpus(new_state == VmState::BreakPoint)
.map_err(Error::CpuManager)?; .map_err(Error::CpuManager)?;
}
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?; let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
*state = new_state; *state = new_state;
@ -2885,9 +2883,10 @@ impl Debuggable for Vm {
} }
fn debug_pause(&mut self) -> std::result::Result<(), DebuggableError> { fn debug_pause(&mut self) -> std::result::Result<(), DebuggableError> {
if !self.cpu_manager.lock().unwrap().vcpus_paused() { if *self.state.read().unwrap() == VmState::Running {
self.pause().map_err(DebuggableError::Pause)?; self.pause().map_err(DebuggableError::Pause)?;
} }
let mut state = self let mut state = self
.state .state
.try_write() .try_write()
@ -2897,25 +2896,10 @@ impl Debuggable for Vm {
} }
fn debug_resume(&mut self) -> std::result::Result<(), DebuggableError> { fn debug_resume(&mut self) -> std::result::Result<(), DebuggableError> {
if !self.cpu_manager.lock().unwrap().vcpus_paused() { if *self.state.read().unwrap() == VmState::BreakPoint {
self.cpu_manager self.resume().map_err(DebuggableError::Pause)?;
.lock()
.unwrap()
.start_boot_vcpus()
.map_err(|e| {
DebuggableError::Resume(MigratableError::Resume(anyhow!(
"Could not start boot vCPUs: {:?}",
e
)))
})?;
} else {
self.resume().map_err(DebuggableError::Resume)?;
} }
let mut state = self
.state
.try_write()
.map_err(|_| DebuggableError::PoisonedState)?;
*state = VmState::Running;
Ok(()) Ok(())
} }