vm-device: support batch update interrupt source group GSI

Split interrupt source group restore into two steps, first restore
the irqfd for each interrupt source entry, and second restore the
GSI routing of the entire interrupt source group.

This patch will reduce restore latency of interrupt source group,
and in a 200-concurrent restore test, the patch reduced the
average IOAPIC restore time from 15ms to 1ms.

Signed-off-by: Yong He <alexyonghe@tencent.com>
This commit is contained in:
Yong He 2023-08-01 14:41:23 +08:00 committed by Rob Bradford
parent ecf3db5092
commit 0149e65081
11 changed files with 66 additions and 4 deletions

View File

@ -98,9 +98,14 @@ impl Gic {
i as InterruptIndex, i as InterruptIndex,
InterruptSourceConfig::LegacyIrq(config), InterruptSourceConfig::LegacyIrq(config),
false, false,
false,
) )
.map_err(Error::EnableInterrupt)?; .map_err(Error::EnableInterrupt)?;
} }
self.interrupt_source_group
.set_gsi()
.map_err(Error::EnableInterrupt)?;
Ok(()) Ok(())
} }

View File

@ -237,9 +237,14 @@ impl Ioapic {
if state.is_some() { if state.is_some() {
for (irq, entry) in ioapic.used_entries.iter().enumerate() { for (irq, entry) in ioapic.used_entries.iter().enumerate() {
if *entry { if *entry {
ioapic.update_entry(irq)?; ioapic.update_entry(irq, false)?;
} }
} }
ioapic
.interrupt_source_group
.set_gsi()
.map_err(Error::UpdateInterrupt)?;
} }
Ok(ioapic) Ok(ioapic)
@ -278,7 +283,7 @@ impl Ioapic {
} }
// The entry must be updated through the interrupt source // The entry must be updated through the interrupt source
// group. // group.
if let Err(e) = self.update_entry(index) { if let Err(e) = self.update_entry(index, true) {
error!("Failed updating IOAPIC entry: {:?}", e); error!("Failed updating IOAPIC entry: {:?}", e);
} }
// Store the information this IRQ is now being used. // Store the information this IRQ is now being used.
@ -329,7 +334,7 @@ impl Ioapic {
} }
} }
fn update_entry(&self, irq: usize) -> Result<()> { fn update_entry(&self, irq: usize, set_gsi: bool) -> Result<()> {
let entry = self.reg_entries[irq]; let entry = self.reg_entries[irq];
// Validate Destination Mode value, and retrieve Destination ID // Validate Destination Mode value, and retrieve Destination ID
@ -386,6 +391,7 @@ impl Ioapic {
irq as InterruptIndex, irq as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
interrupt_mask(entry) == 1, interrupt_mask(entry) == 1,
set_gsi,
) )
.map_err(Error::UpdateInterrupt)?; .map_err(Error::UpdateInterrupt)?;

View File

@ -361,10 +361,15 @@ mod tests {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> result::Result<(), std::io::Error> { ) -> result::Result<(), std::io::Error> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap()) Some(self.event_fd.try_clone().unwrap())
} }

View File

@ -413,10 +413,15 @@ mod tests {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> result::Result<(), std::io::Error> { ) -> result::Result<(), std::io::Error> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap()) Some(self.event_fd.try_clone().unwrap())
} }

View File

@ -365,9 +365,13 @@ mod tests {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> result::Result<(), std::io::Error> { ) -> result::Result<(), std::io::Error> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap()) Some(self.event_fd.try_clone().unwrap())
} }

View File

@ -485,9 +485,13 @@ mod tests {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> result::Result<(), std::io::Error> { ) -> result::Result<(), std::io::Error> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap()) Some(self.event_fd.try_clone().unwrap())
} }

View File

@ -59,9 +59,13 @@ impl InterruptSourceGroup for TestInterrupt {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap()) Some(self.event_fd.try_clone().unwrap())
} }

View File

@ -205,10 +205,15 @@ impl MsiConfig {
idx as InterruptIndex, idx as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
state.cap.vector_masked(idx), state.cap.vector_masked(idx),
false,
) )
.map_err(Error::UpdateInterruptRoute)?; .map_err(Error::UpdateInterruptRoute)?;
} }
interrupt_source_group
.set_gsi()
.map_err(Error::EnableInterruptRoute)?;
interrupt_source_group interrupt_source_group
.enable() .enable()
.map_err(Error::EnableInterruptRoute)?; .map_err(Error::EnableInterruptRoute)?;
@ -262,6 +267,7 @@ impl MsiConfig {
idx as InterruptIndex, idx as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
self.cap.vector_masked(idx), self.cap.vector_masked(idx),
true,
) { ) {
error!("Failed updating vector: {:?}", e); error!("Failed updating vector: {:?}", e);
} }

View File

@ -107,6 +107,7 @@ impl MsixConfig {
idx as InterruptIndex, idx as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
state.masked, state.masked,
true,
) )
.map_err(Error::UpdateInterruptRoute)?; .map_err(Error::UpdateInterruptRoute)?;
@ -182,6 +183,7 @@ impl MsixConfig {
idx as InterruptIndex, idx as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
table_entry.masked(), table_entry.masked(),
true,
) { ) {
error!("Failed updating vector: {:?}", e); error!("Failed updating vector: {:?}", e);
} }
@ -320,6 +322,7 @@ impl MsixConfig {
index as InterruptIndex, index as InterruptIndex,
InterruptSourceConfig::MsiIrq(config), InterruptSourceConfig::MsiIrq(config),
table_entry.masked(), table_entry.masked(),
true,
) { ) {
error!("Failed updating vector: {:?}", e); error!("Failed updating vector: {:?}", e);
} }

View File

@ -147,10 +147,15 @@ pub trait InterruptSourceGroup: Send + Sync {
/// * index: sub-index into the group. /// * index: sub-index into the group.
/// * config: configuration data for the interrupt source. /// * config: configuration data for the interrupt source.
/// * masked: if the interrupt is masked /// * masked: if the interrupt is masked
/// * set_gsi: whehter update the GSI routing table.
fn update( fn update(
&self, &self,
index: InterruptIndex, index: InterruptIndex,
config: InterruptSourceConfig, config: InterruptSourceConfig,
masked: bool, masked: bool,
set_gsi: bool,
) -> Result<()>; ) -> Result<()>;
/// Set the interrupt group GSI routing table.
fn set_gsi(&self) -> Result<()>;
} }

View File

@ -170,6 +170,7 @@ impl InterruptSourceGroup for MsiInterruptGroup {
index: InterruptIndex, index: InterruptIndex,
config: InterruptSourceConfig, config: InterruptSourceConfig,
masked: bool, masked: bool,
set_gsi: bool,
) -> Result<()> { ) -> Result<()> {
if let Some(route) = self.irq_routes.get(&index) { if let Some(route) = self.irq_routes.get(&index) {
let entry = RoutingEntry { let entry = RoutingEntry {
@ -183,7 +184,11 @@ impl InterruptSourceGroup for MsiInterruptGroup {
} }
let mut routes = self.gsi_msi_routes.lock().unwrap(); let mut routes = self.gsi_msi_routes.lock().unwrap();
routes.insert(route.gsi, entry); routes.insert(route.gsi, entry);
return self.set_gsi_routes(&routes); if set_gsi {
return self.set_gsi_routes(&routes);
} else {
return Ok(());
}
} }
Err(io::Error::new( Err(io::Error::new(
@ -191,6 +196,11 @@ impl InterruptSourceGroup for MsiInterruptGroup {
format!("update: Invalid interrupt index {index}"), format!("update: Invalid interrupt index {index}"),
)) ))
} }
fn set_gsi(&self) -> Result<()> {
let routes = self.gsi_msi_routes.lock().unwrap();
self.set_gsi_routes(&routes)
}
} }
pub struct LegacyUserspaceInterruptGroup { pub struct LegacyUserspaceInterruptGroup {
@ -223,10 +233,15 @@ impl InterruptSourceGroup for LegacyUserspaceInterruptGroup {
_index: InterruptIndex, _index: InterruptIndex,
_config: InterruptSourceConfig, _config: InterruptSourceConfig,
_masked: bool, _masked: bool,
_set_gsi: bool,
) -> Result<()> { ) -> Result<()> {
Ok(()) Ok(())
} }
fn set_gsi(&self) -> Result<()> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> { fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
self.ioapic.lock().unwrap().notifier(self.irq as usize) self.ioapic.lock().unwrap().notifier(self.irq as usize)
} }