mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-13 15:14:08 +00:00
Merge pull request #11144 from Apokleos/hotplug-block-qemu-rs
Support hot-plug block device in qemu-rs with QMP
This commit is contained in:
commit
5378e581d8
@ -618,6 +618,20 @@ impl QemuInner {
|
|||||||
)?;
|
)?;
|
||||||
qmp.hotplug_network_device(&netdev, &virtio_net_device)?
|
qmp.hotplug_network_device(&netdev, &virtio_net_device)?
|
||||||
}
|
}
|
||||||
|
DeviceType::Block(mut block_device) => {
|
||||||
|
block_device.config.pci_path = qmp
|
||||||
|
.hotplug_block_device(
|
||||||
|
&self.config.blockdev_info.block_device_driver,
|
||||||
|
&block_device.device_id,
|
||||||
|
&block_device.config.path_on_host,
|
||||||
|
block_device.config.is_direct,
|
||||||
|
block_device.config.is_readonly,
|
||||||
|
block_device.config.no_drop,
|
||||||
|
)
|
||||||
|
.context("hotplug block device")?;
|
||||||
|
|
||||||
|
return Ok(DeviceType::Block(block_device));
|
||||||
|
}
|
||||||
_ => info!(sl!(), "hotplugging of {:#?} is unsupported", device),
|
_ => info!(sl!(), "hotplugging of {:#?} is unsupported", device),
|
||||||
}
|
}
|
||||||
Ok(device)
|
Ok(device)
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use crate::device::pci_path::PciPath;
|
||||||
use crate::qemu::cmdline_generator::{DeviceVirtioNet, Netdev};
|
use crate::qemu::cmdline_generator::{DeviceVirtioNet, Netdev};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags};
|
use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags};
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::os::fd::{AsRawFd, RawFd};
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
@ -14,7 +16,7 @@ use std::os::unix::net::UnixStream;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use qapi::qmp;
|
use qapi::qmp;
|
||||||
use qapi_qmp;
|
use qapi_qmp::{self, PciDeviceInfo};
|
||||||
use qapi_spec::Dictionary;
|
use qapi_spec::Dictionary;
|
||||||
|
|
||||||
pub struct Qmp {
|
pub struct Qmp {
|
||||||
@ -467,8 +469,163 @@ impl Qmp {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_device_by_qdev_id(&mut self, qdev_id: &str) -> Result<PciPath> {
|
||||||
|
let format_str = |vec: &Vec<i64>| -> String {
|
||||||
|
vec.iter()
|
||||||
|
.map(|num| format!("{:02x}", num))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("/")
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut path = vec![];
|
||||||
|
let pci = self.qmp.execute(&qapi_qmp::query_pci {})?;
|
||||||
|
for pci_info in pci.iter() {
|
||||||
|
if let Some(_device) = get_pci_path_by_qdev_id(&pci_info.devices, qdev_id, &mut path) {
|
||||||
|
let pci_path = format_str(&path);
|
||||||
|
return PciPath::try_from(pci_path.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("no target device found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// hotplug block device:
|
||||||
|
/// {
|
||||||
|
/// "execute": "blockdev-add",
|
||||||
|
/// "arguments": {
|
||||||
|
/// "node-name": "drive-0",
|
||||||
|
/// "file": {"driver": "file", "filename": "/path/to/block"},
|
||||||
|
/// "cache": {"direct": true},
|
||||||
|
/// "read-only": false
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// "execute": "device_add",
|
||||||
|
/// "arguments": {
|
||||||
|
/// "id": "drive-0",
|
||||||
|
/// "driver": "virtio-blk-pci",
|
||||||
|
/// "drive": "drive-0",
|
||||||
|
/// "addr":"0x0",
|
||||||
|
/// "bus": "pcie.1"
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
pub fn hotplug_block_device(
|
||||||
|
&mut self,
|
||||||
|
block_driver: &str,
|
||||||
|
device_id: &str,
|
||||||
|
path_on_host: &str,
|
||||||
|
is_direct: Option<bool>,
|
||||||
|
is_readonly: bool,
|
||||||
|
no_drop: bool,
|
||||||
|
) -> Result<Option<PciPath>> {
|
||||||
|
let (bus, slot) = self.find_free_slot()?;
|
||||||
|
|
||||||
|
// `blockdev-add`
|
||||||
|
let node_name = format!("drive-{}", device_id);
|
||||||
|
self.qmp
|
||||||
|
.execute(&qmp::blockdev_add(qmp::BlockdevOptions::raw {
|
||||||
|
base: qmp::BlockdevOptionsBase {
|
||||||
|
detect_zeroes: None,
|
||||||
|
cache: None,
|
||||||
|
discard: None,
|
||||||
|
force_share: None,
|
||||||
|
auto_read_only: None,
|
||||||
|
node_name: Some(node_name.clone()),
|
||||||
|
read_only: None,
|
||||||
|
},
|
||||||
|
raw: qmp::BlockdevOptionsRaw {
|
||||||
|
base: qmp::BlockdevOptionsGenericFormat {
|
||||||
|
file: qmp::BlockdevRef::definition(Box::new(qmp::BlockdevOptions::file {
|
||||||
|
base: qapi_qmp::BlockdevOptionsBase {
|
||||||
|
auto_read_only: None,
|
||||||
|
cache: if is_direct.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(qapi_qmp::BlockdevCacheOptions {
|
||||||
|
direct: is_direct,
|
||||||
|
no_flush: None,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
detect_zeroes: None,
|
||||||
|
discard: None,
|
||||||
|
force_share: None,
|
||||||
|
node_name: None,
|
||||||
|
read_only: Some(is_readonly),
|
||||||
|
},
|
||||||
|
file: qapi_qmp::BlockdevOptionsFile {
|
||||||
|
aio: None,
|
||||||
|
aio_max_batch: None,
|
||||||
|
drop_cache: if !no_drop { None } else { Some(no_drop) },
|
||||||
|
locking: None,
|
||||||
|
pr_manager: None,
|
||||||
|
x_check_cache_dropped: None,
|
||||||
|
filename: path_on_host.to_owned(),
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
offset: None,
|
||||||
|
size: None,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.map_err(|e| anyhow!("blockdev_add {:?}", e))
|
||||||
|
.map(|_| ())?;
|
||||||
|
|
||||||
|
// `device_add`
|
||||||
|
let mut blkdev_add_args = Dictionary::new();
|
||||||
|
blkdev_add_args.insert("addr".to_owned(), format!("{:02}", slot).into());
|
||||||
|
blkdev_add_args.insert("drive".to_owned(), node_name.clone().into());
|
||||||
|
self.qmp
|
||||||
|
.execute(&qmp::device_add {
|
||||||
|
bus: Some(bus),
|
||||||
|
id: Some(node_name.clone()),
|
||||||
|
driver: block_driver.to_string(),
|
||||||
|
arguments: blkdev_add_args,
|
||||||
|
})
|
||||||
|
.map_err(|e| anyhow!("device_add {:?}", e))
|
||||||
|
.map(|_| ())?;
|
||||||
|
|
||||||
|
let pci_path = self
|
||||||
|
.get_device_by_qdev_id(&node_name)
|
||||||
|
.context("get device by qdev_id failed")?;
|
||||||
|
info!(
|
||||||
|
sl!(),
|
||||||
|
"hotplug_block_device return pci path: {:?}", &pci_path
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Some(pci_path))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vcpu_id_from_core_id(core_id: i64) -> String {
|
fn vcpu_id_from_core_id(core_id: i64) -> String {
|
||||||
format!("cpu-{}", core_id)
|
format!("cpu-{}", core_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The get_pci_path_by_qdev_id function searches a device list for a device matching a given qdev_id,
|
||||||
|
// tracking the device's path. It recursively explores bridge devices and returns the found device along
|
||||||
|
// with its updated path.
|
||||||
|
pub fn get_pci_path_by_qdev_id(
|
||||||
|
devices: &[PciDeviceInfo],
|
||||||
|
qdev_id: &str,
|
||||||
|
path: &mut Vec<i64>,
|
||||||
|
) -> Option<PciDeviceInfo> {
|
||||||
|
for device in devices {
|
||||||
|
path.push(device.slot);
|
||||||
|
if device.qdev_id == qdev_id {
|
||||||
|
return Some(device.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref bridge) = device.pci_bridge {
|
||||||
|
if let Some(ref bridge_devices) = bridge.devices {
|
||||||
|
if let Some(found_device) = get_pci_path_by_qdev_id(bridge_devices, qdev_id, path) {
|
||||||
|
return Some(found_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device not found, pop the current slot before moving to next device
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user