Signals all vCPU threads and waits for them to ACK the interruption. Calls [`VcpuState::signal_thread`] and [`VcpuState::wait_until_signal_acknowledged`] for each vCPU.
(&mut self)
| 1584 | /// Calls [`VcpuState::signal_thread`] and |
| 1585 | /// [`VcpuState::wait_until_signal_acknowledged`] for each vCPU. |
| 1586 | fn signal_vcpus(&mut self) -> Result<()> { |
| 1587 | let vcpu_count = self.vcpu_states.lock().unwrap().len(); |
| 1588 | |
| 1589 | // Splitting this into two loops reduced the time to pause many vCPUs |
| 1590 | // massively. Example: 254 vCPUs. >254ms -> ~4ms. |
| 1591 | // |
| 1592 | // Do not hold `vcpu_states` across the wait phase. A vCPU can handle |
| 1593 | // the kick in userspace while servicing MMIO on the ACPI CPU hotplug |
| 1594 | // device, and that path also takes `vcpu_states`. Holding the mutex |
| 1595 | // here while waiting would deadlock pause against that MMIO access. |
| 1596 | for cpu_id in 0..vcpu_count { |
| 1597 | let vcpu_states = self.vcpu_states.lock().unwrap(); |
| 1598 | vcpu_states[cpu_id].signal_thread(); |
| 1599 | } |
| 1600 | for cpu_id in 0..vcpu_count { |
| 1601 | let vcpu_states = self.vcpu_states.lock().unwrap(); |
| 1602 | vcpu_states[cpu_id].wait_until_signal_acknowledged()?; |
| 1603 | } |
| 1604 | |
| 1605 | Ok(()) |
| 1606 | } |
| 1607 | |
| 1608 | pub fn shutdown(&mut self) -> Result<()> { |
| 1609 | // Tell the vCPUs to stop themselves next time they go through the loop |
no test coverage detected