From 30a64092a7ebc4633744f817f096d908ead7f264 Mon Sep 17 00:00:00 2001 From: Hyounggyu Choi Date: Thu, 21 Nov 2024 13:03:51 +0100 Subject: [PATCH] runtime-rs: Add CcwSubChannel to provide devno for CCW devices To explicitly specify a device number on the QEMU command line for the following devices using the CCW transport on s390x: - SerialDevice - BlockDevice - VhostUserDevice - SCSIController - VSOCKDevice this commit introduces a new structure CcwSubChannel and implements the following methods: - add_device() - remove_device() - address_format_ccw() - set_addr() You can see the detailed explanation for each method in the comment. This resolves the 1st part of #10573. Signed-off-by: Hyounggyu Choi --- .../hypervisor/src/qemu/cmdline_generator.rs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs index 7e7e328124..ff01d95ee4 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs @@ -369,6 +369,88 @@ impl ToQemuParams for Cpu { } } +/// Error type for CCW Subchannel operations +#[derive(Debug)] +enum CcwError { + DeviceAlreadyExists(String), // Error when trying to add an existing device + #[allow(dead_code)] + DeviceNotFound(String), // Error when trying to remove a nonexistent device +} + +/// Represents a CCW subchannel for managing devices +#[derive(Debug)] +struct CcwSubChannel { + devices: HashMap, // Maps device IDs to slot indices + addr: u32, // Subchannel address + next_slot: u32, // Next available slot index +} + +impl CcwSubChannel { + fn new() -> Self { + Self { + devices: HashMap::new(), + addr: 0, + next_slot: 0, + } + } + + /// Adds a device to the subchannel. + /// + /// # Arguments + /// - `dev_id`: device ID to add + /// + /// # Returns + /// - `Result`: slot index of the added device + /// or an error if the device already exists + fn add_device(&mut self, dev_id: &str) -> Result { + if self.devices.contains_key(dev_id) { + Err(CcwError::DeviceAlreadyExists(dev_id.to_owned())) + } else { + let slot = self.next_slot; + self.devices.insert(dev_id.to_owned(), slot); + self.next_slot += 1; + Ok(slot) + } + } + + /// Removes a device from the subchannel by its ID. + /// + /// # Arguments + /// - `dev_id`: device ID to remove + /// + /// # Returns + /// - `Result<(), CcwError>`: Ok(()) if the device was removed + /// or an error if the device was not found + #[allow(dead_code)] + fn remove_device(&mut self, dev_id: &str) -> Result<(), CcwError> { + if self.devices.remove(dev_id).is_some() { + Ok(()) + } else { + Err(CcwError::DeviceNotFound(dev_id.to_owned())) + } + } + + /// Formats the CCW address for a given slot + /// + /// # Arguments + /// - `slot`: slot index + /// + /// # Returns + /// - `String`: formatted CCW address (e.g. `fe.0.0000`) + fn address_format_ccw(&self, slot: u32) -> String { + format!("fe.{:x}.{:04x}", self.addr, slot) + } + + /// Sets the address of the subchannel. + /// # Arguments + /// - `addr`: subchannel address to set + #[allow(dead_code)] + fn set_addr(&mut self, addr: u32) -> &mut Self { + self.addr = addr; + self + } +} + #[derive(Debug)] struct Machine { r#type: String, @@ -1622,10 +1704,15 @@ pub struct QemuCmdLine<'a> { knobs: Knobs, devices: Vec>, + ccw_subchannel: Option, } impl<'a> QemuCmdLine<'a> { pub fn new(id: &str, config: &'a HypervisorConfig) -> Result> { + let ccw_subchannel = match bus_type(config) { + VirtioBusType::Ccw => Some(CcwSubChannel::new()), + _ => None, + }; let mut qemu_cmd_line = QemuCmdLine { id: id.to_string(), config, @@ -1637,6 +1724,7 @@ impl<'a> QemuCmdLine<'a> { qmp_socket: QmpSocket::new(MonitorProtocol::Qmp)?, knobs: Knobs::new(config), devices: Vec::new(), + ccw_subchannel, }; if config.device_info.enable_iommu {