mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-30 14:25:43 +00:00
runtime-rs: implement netdev hotplugging for qemu-rs
With the helpers from previous commit, the actual hotplugging implementation, though lengthy, is mostly just assembling a QMP command to hotplug the network device backend and then doing the same for the corresponding frontend. Note that hotplug_network_device() takes cmdline_generator types Netdev and DeviceVirtioNet. This is intentional and aims to take advantage of the similarity between parameter sets needed to coldplug and hotplug devices reuse and simplify our code. To enable using the types from qmp, accessors were added as needed. Signed-off-by: Pavel Mores <pmores@redhat.com>
This commit is contained in:
parent
4eb7e2966c
commit
ac393f6316
@ -1021,6 +1021,18 @@ impl Netdev {
|
||||
self.disable_vhost_net = disable_vhost_net;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> &String {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn get_fds(&self) -> &Vec<File> {
|
||||
&self.fds["fds"]
|
||||
}
|
||||
|
||||
pub fn get_vhostfds(&self) -> &Vec<File> {
|
||||
&self.fds["vhostfds"]
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -1089,6 +1101,26 @@ impl DeviceVirtioNet {
|
||||
self.iommu_platform = iommu_platform;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_netdev_id(&self) -> &String {
|
||||
&self.netdev_id
|
||||
}
|
||||
|
||||
pub fn get_device_driver(&self) -> &String {
|
||||
&self.device_driver
|
||||
}
|
||||
|
||||
pub fn get_mac_addr(&self) -> String {
|
||||
format!("{:?}", self.mac_address)
|
||||
}
|
||||
|
||||
pub fn get_num_queues(&self) -> u32 {
|
||||
self.num_queues
|
||||
}
|
||||
|
||||
pub fn get_disable_modern(&self) -> bool {
|
||||
self.disable_modern
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -3,6 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::qemu::cmdline_generator::{DeviceVirtioNet, Netdev};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags};
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
@ -294,7 +296,6 @@ impl Qmp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn find_free_slot(&mut self) -> Result<(String, i64)> {
|
||||
let pci = self.qmp.execute(&qapi_qmp::query_pci {})?;
|
||||
for pci_info in &pci {
|
||||
@ -336,7 +337,6 @@ impl Qmp {
|
||||
Err(anyhow!("no free slots on PCI bridges"))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pass_fd(&mut self, fd: RawFd, fdname: &str) -> Result<()> {
|
||||
info!(sl!(), "passing fd {:?} as {}", fd, fdname);
|
||||
|
||||
@ -372,6 +372,102 @@ impl Qmp {
|
||||
Err(err) => Err(anyhow!("failed to pass {} ({}): {}", fdname, fd, err)),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn hotplug_network_device(
|
||||
&mut self,
|
||||
netdev: &Netdev,
|
||||
virtio_net_device: &DeviceVirtioNet,
|
||||
) -> Result<()> {
|
||||
debug!(
|
||||
sl!(),
|
||||
"hotplug_network_device(): PCI before {}: {:#?}",
|
||||
virtio_net_device.get_netdev_id(),
|
||||
self.qmp.execute(&qapi_qmp::query_pci {})?
|
||||
);
|
||||
|
||||
let (bus, slot) = self.find_free_slot()?;
|
||||
|
||||
let mut fd_names = vec![];
|
||||
for (idx, fd) in netdev.get_fds().iter().enumerate() {
|
||||
let fdname = format!("fd{}", idx);
|
||||
self.pass_fd(fd.as_raw_fd(), fdname.as_ref())?;
|
||||
fd_names.push(fdname);
|
||||
}
|
||||
|
||||
let mut vhostfd_names = vec![];
|
||||
for (idx, fd) in netdev.get_vhostfds().iter().enumerate() {
|
||||
let vhostfdname = format!("vhostfd{}", idx);
|
||||
self.pass_fd(fd.as_raw_fd(), vhostfdname.as_ref())?;
|
||||
vhostfd_names.push(vhostfdname);
|
||||
}
|
||||
|
||||
self.qmp
|
||||
.execute(&qapi_qmp::netdev_add(qapi_qmp::Netdev::tap {
|
||||
id: netdev.get_id().clone(),
|
||||
tap: qapi_qmp::NetdevTapOptions {
|
||||
br: None,
|
||||
downscript: None,
|
||||
fd: None,
|
||||
// Logic in cmdline_generator::Netdev::new() seems to
|
||||
// guarantee that there will always be at least one fd.
|
||||
fds: Some(fd_names.join(",")),
|
||||
helper: None,
|
||||
ifname: None,
|
||||
poll_us: None,
|
||||
queues: None,
|
||||
script: None,
|
||||
sndbuf: None,
|
||||
vhost: if vhostfd_names.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(true)
|
||||
},
|
||||
vhostfd: None,
|
||||
vhostfds: if vhostfd_names.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(vhostfd_names.join(","))
|
||||
},
|
||||
vhostforce: None,
|
||||
vnet_hdr: None,
|
||||
},
|
||||
}))?;
|
||||
|
||||
let mut netdev_frontend_args = Dictionary::new();
|
||||
netdev_frontend_args.insert(
|
||||
"netdev".to_owned(),
|
||||
virtio_net_device.get_netdev_id().clone().into(),
|
||||
);
|
||||
netdev_frontend_args.insert("addr".to_owned(), format!("{:02}", slot).into());
|
||||
netdev_frontend_args.insert("mac".to_owned(), virtio_net_device.get_mac_addr().into());
|
||||
netdev_frontend_args.insert("mq".to_owned(), "on".into());
|
||||
// As the golang runtime documents the vectors computation, it's
|
||||
// 2N+2 vectors, N for tx queues, N for rx queues, 1 for config, and one for possible control vq
|
||||
netdev_frontend_args.insert(
|
||||
"vectors".to_owned(),
|
||||
(2 * virtio_net_device.get_num_queues() + 2).into(),
|
||||
);
|
||||
if virtio_net_device.get_disable_modern() {
|
||||
netdev_frontend_args.insert("disable-modern".to_owned(), true.into());
|
||||
}
|
||||
|
||||
self.qmp.execute(&qmp::device_add {
|
||||
bus: Some(bus),
|
||||
id: Some(format!("frontend-{}", virtio_net_device.get_netdev_id())),
|
||||
driver: virtio_net_device.get_device_driver().clone(),
|
||||
arguments: netdev_frontend_args,
|
||||
})?;
|
||||
|
||||
debug!(
|
||||
sl!(),
|
||||
"hotplug_network_device(): PCI after {}: {:#?}",
|
||||
virtio_net_device.get_netdev_id(),
|
||||
self.qmp.execute(&qapi_qmp::query_pci {})?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn vcpu_id_from_core_id(core_id: i64) -> String {
|
||||
|
Loading…
Reference in New Issue
Block a user