Skip to content

Commit fcea4cd

Browse files
committed
Implement hot-unplug on the Guest side
Guest side hot-unplug implementation. This means that the API call can be made, and the vCPUs are successfully removed from the guest, but the backing vCPU threads are not removed from the host VMM. As a result of this, hot-plugs that occur after hot-unplugs do not work correctly right now. Once the total thread count in the VMM exceeds 32, there is no effect of hot-plugging. To complete this, a refactor is needed of the VMM, such that the CpuContainer can somehow remove the threads when the guest kernel calls _EJ0. Signed-off-by: James Curtis <[email protected]>
1 parent 566082e commit fcea4cd

File tree

3 files changed

+123
-7
lines changed

3 files changed

+123
-7
lines changed

src/vmm/src/devices/acpi/cpu_container.rs

+50-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ pub const CPU_CONTAINER_ACPI_SIZE: usize = 0xC;
5151

5252
const CPU_ENABLE_BIT: u8 = 1 << 0;
5353
const CPU_INSERTING_BIT: u8 = 1 << 1;
54+
const CPU_REMOVING_BIT: u8 = 1 << 2;
55+
const CPU_EJECT_BIT: u8 = 1 << 3;
5456

5557
const CPU_SELECTION_OFFSET: u64 = 0;
5658
const CPU_STATUS_OFFSET: u64 = 4;
@@ -94,6 +96,7 @@ impl CpuContainer {
9496
cpu_id: i,
9597
active: i < boot_count,
9698
inserting: false,
99+
removing: false,
97100
})
98101
}
99102

@@ -123,6 +126,9 @@ impl CpuContainer {
123126
if cpu_device.inserting {
124127
data[0] |= CPU_INSERTING_BIT;
125128
}
129+
if cpu_device.removing {
130+
data[0] |= CPU_REMOVING_BIT;
131+
}
126132
} else {
127133
error!("Out of range vCPU id: {}", self.selected_cpu)
128134
}
@@ -143,6 +149,10 @@ impl CpuContainer {
143149
if data[0] & CPU_ENABLE_BIT != 0 {
144150
cpu_device.active = true;
145151
}
152+
if data[0] & CPU_EJECT_BIT != 0 {
153+
cpu_device.active = false;
154+
// TODO: Remove vCPU handle from VMM
155+
}
146156
} else {
147157
error!("Out of range vCPU id: {}", self.selected_cpu)
148158
}
@@ -215,7 +225,9 @@ impl Aml for CpuContainer {
215225
aml::FieldEntry::Reserved(32),
216226
aml::FieldEntry::Named(*b"CPEN", 1),
217227
aml::FieldEntry::Named(*b"CINS", 1),
218-
aml::FieldEntry::Reserved(6),
228+
aml::FieldEntry::Named(*b"CRMV", 1),
229+
aml::FieldEntry::Named(*b"CEJ0", 1),
230+
aml::FieldEntry::Reserved(4),
219231
aml::FieldEntry::Named(*b"CCMD", 8),
220232
],
221233
),
@@ -270,6 +282,8 @@ pub struct CpuDevice {
270282
pub active: bool,
271283
/// Whether this CPU is in the process of being inserted
272284
pub inserting: bool,
285+
/// Whether this CPU is in the process of being removed
286+
pub removing: bool,
273287
}
274288

275289
impl CpuDevice {
@@ -305,6 +319,16 @@ impl Aml for CpuDevice {
305319
))],
306320
),
307321
&aml::Name::new("_MAT".into(), &aml::Buffer::new(mat_data)),
322+
&aml::Method::new(
323+
"_EJ0".into(),
324+
1,
325+
false,
326+
// Call into CEJ0 method which will actually eject device
327+
vec![&aml::MethodCall::new(
328+
"\\_SB_.CPUS.CEJ0".into(),
329+
vec![&self.cpu_id],
330+
)],
331+
),
308332
],
309333
)
310334
.append_aml_bytes(v)
@@ -320,7 +344,7 @@ impl Aml for CpuNotify {
320344
let object = aml::Path::new(&format!("C{:03X}", self.cpu_id));
321345
aml::If::new(
322346
&aml::Equal::new(&aml::Arg(0), &self.cpu_id),
323-
vec![&aml::Notify::new(&object, &1u8)],
347+
vec![&aml::Notify::new(&object, &aml::Arg(1))],
324348
)
325349
.append_aml_bytes(v)
326350
}
@@ -369,6 +393,21 @@ impl Aml for CpuMethods {
369393

370394
aml::Method::new("CTFY".into(), 2, true, cpu_notifies_refs).append_aml_bytes(v);
371395

396+
aml::Method::new(
397+
"CEJ0".into(),
398+
1,
399+
true,
400+
vec![
401+
&aml::Acquire::new("\\_SB_.PRES.CPLK".into(), 0xffff),
402+
// Write CPU number (in first argument) to I/O port via field
403+
&aml::Store::new(&aml::Path::new("\\_SB_.PRES.CSEL"), &aml::Arg(0)),
404+
// Set CEJ0 bit
405+
&aml::Store::new(&aml::Path::new("\\_SB_.PRES.CEJ0"), &aml::ONE),
406+
&aml::Release::new("\\_SB_.PRES.CPLK".into()),
407+
],
408+
)
409+
.append_aml_bytes(v);
410+
372411
aml::Method::new(
373412
"CSCN".into(),
374413
0,
@@ -396,6 +435,15 @@ impl Aml for CpuMethods {
396435
&aml::Store::new(&aml::Path::new("\\_SB_.PRES.CINS"), &aml::ONE),
397436
],
398437
),
438+
&aml::If::new(
439+
&aml::Equal::new(&aml::Path::new("\\_SB_.PRES.CRMV"), &aml::ONE),
440+
// Notify device if it is (with the eject constant 0x3)
441+
vec![
442+
&aml::MethodCall::new("CTFY".into(), vec![&aml::Local(0), &3u8]),
443+
// Reset CRMV bit
444+
&aml::Store::new(&aml::Path::new("\\_SB_.PRES.CRMV"), &aml::ONE),
445+
],
446+
),
399447
&aml::Add::new(&aml::Local(0), &aml::Local(0), &aml::ONE),
400448
],
401449
),

src/vmm/src/lib.rs

+46
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,52 @@ impl Vmm {
682682
}
683683

684684
/// Removes vCPUs from VMM.
685+
#[cfg(target_arch = "x86_64")]
686+
pub fn hotunplug_vcpus(
687+
&mut self,
688+
config: HotplugVcpuConfig,
689+
) -> Result<MachineConfigUpdate, HotplugVcpuError> {
690+
use crate::logger::IncMetric;
691+
if config.target < 1 {
692+
return Err(HotplugVcpuError::VcpuCountTooLow);
693+
}
694+
if let Some(kvm_config) = self.vcpu_config.as_mut() {
695+
kvm_config.vcpu_count = config.target;
696+
}
697+
698+
#[allow(clippy::cast_possible_truncation)]
699+
let start_idx: u8 = config.target;
700+
if let Some(devices::BusDevice::CpuContainer(cont)) =
701+
self.get_bus_device(DeviceType::CpuContainer, "CpuContainer")
702+
{
703+
let mut locked_container = cont.lock().expect("Poisoned lock");
704+
for cpu_idx in start_idx..(self.vcpus_handles.len() as u8) {
705+
locked_container.cpu_devices[cpu_idx as usize].removing = true;
706+
}
707+
}
708+
709+
#[allow(clippy::cast_lossless)]
710+
METRICS
711+
.hotplug
712+
.vcpus_added
713+
.add(self.vcpus_handles.len() as u64 - config.target as u64);
714+
715+
// Update VM config to reflect new CPUs added
716+
#[allow(clippy::cast_possible_truncation)]
717+
let new_machine_config = MachineConfigUpdate {
718+
vcpu_count: Some(self.vcpus_handles.len() as u8),
719+
mem_size_mib: None,
720+
smt: None,
721+
cpu_template: None,
722+
track_dirty_pages: None,
723+
huge_pages: None,
724+
};
725+
726+
self.acpi_device_manager.notify_cpu_container()?;
727+
728+
Ok(new_machine_config)
729+
}
730+
685731
/// Retrieves the KVM dirty bitmap for each of the guest's memory regions.
686732
pub fn reset_dirty_bitmap(&self) {
687733
self.guest_memory

src/vmm/src/rpc_interface.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,17 @@ impl RuntimeApiController {
670670
self.vmm.lock().expect("Poisoned lock").version(),
671671
)),
672672
#[cfg(target_arch = "x86_64")]
673-
HotplugRequest(request_type) => self.handle_hotplug_request(request_type),
673+
HotplugRequest(request_type) => {
674+
let curr_vcpus: u8 = self
675+
.vmm
676+
.lock()
677+
.expect("Poisoned lock")
678+
.vcpus_handles
679+
.len()
680+
.try_into()
681+
.unwrap();
682+
self.handle_hotplug_request(request_type, curr_vcpus)
683+
}
674684
PatchMMDS(value) => self.patch_mmds(value),
675685
Pause => self.pause(),
676686
PutMMDS(value) => self.put_mmds(value),
@@ -872,13 +882,25 @@ impl RuntimeApiController {
872882
fn handle_hotplug_request(
873883
&mut self,
874884
cfg: HotplugRequestConfig,
885+
curr_vcpus: u8,
875886
) -> Result<VmmData, VmmActionError> {
876887
match cfg {
877888
HotplugRequestConfig::Vcpu(cfg) => {
878-
let result = self.vmm.lock().expect("Poisoned lock").hotplug_vcpus(cfg);
879-
result
880-
.map_err(|err| VmmActionError::HotplugRequest(HotplugRequestError::Vcpu(err)))
881-
.and_then(|machine_cfg_update| self.update_vm_config(machine_cfg_update))
889+
if cfg.target > curr_vcpus {
890+
let result = self.vmm.lock().expect("Poisoned lock").hotplug_vcpus(cfg);
891+
result
892+
.map_err(|err| {
893+
VmmActionError::HotplugRequest(HotplugRequestError::Vcpu(err))
894+
})
895+
.and_then(|machine_cfg_update| self.update_vm_config(machine_cfg_update))
896+
} else {
897+
let result = self.vmm.lock().expect("Poisoned lock").hotunplug_vcpus(cfg);
898+
result
899+
.map_err(|err| {
900+
VmmActionError::HotplugRequest(HotplugRequestError::Vcpu(err))
901+
})
902+
.and_then(|machine_cfg_update| self.update_vm_config(machine_cfg_update))
903+
}
882904
}
883905
}
884906
}

0 commit comments

Comments
 (0)