diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs b/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs index e4069c86c6..8b823d8161 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs @@ -6,7 +6,7 @@ use crate::device::pci_path::PciPath; use crate::qemu::cmdline_generator::{DeviceVirtioNet, Netdev}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags}; use std::convert::TryFrom; use std::fmt::{Debug, Error, Formatter}; @@ -489,6 +489,113 @@ impl Qmp { 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, + is_readonly: bool, + no_drop: bool, + ) -> Result> { + 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 {